R_SOCKET(3)

πŸ’– R_SOCKET πŸ’– β€” the network socket library for radare2, bestie

okay so real talk, this is the page for r_socket, the network socket library that lives inside radare2 (you'd find it in section 3 of the manual, which is the "library functions" section, iykyk) πŸ’…


✨ SYNOPSIS ✨

#include <r_socket.h>

πŸ¦‹ DESCRIPTION πŸ¦‹

So like, all the networking and local process I/O that gets used everywhere across radare2? Yeah, that's the r_socket API serving it up. It's a small, portable socket wrapper that handles plain TCP/UDP, SSL/TLS (when it's compiled with OpenSSL, no cap), Unix domain sockets, and the serial + virtual protocols (that's RAP and r2pipe, iconic behavior). The API is intentionally smol on purpose so the higher level components in the tree β€” like libr/core and libr/main β€” can just combine connect/listen, HTTP helpers and process pipes to build r2web, r2agent and remote file access. it's giving modular πŸ’…

The main opaque type is struct r_socket_t, and it's holding the underlying file descriptor plus an optional SSL context when TLS is in the picture. the way it just carries all that… 🫢


🌸 INITIALIZATION 🌸

Before you do literally anything socket-y, allocate an RSocket β€” and free it when you're done, bestie, please 😭. The constructor lets you ask for an SSL-capable socket, and if you made it with is_ssl=true it will attempt TLS negotiation on connect or accept when OpenSSL is available.

Function signatures:

RSocket *r_socket_new (bool is_ssl);
RSocket *r_socket_new_from_fd (int fd);
void r_socket_free (RSocket *s);

r_socket_new () never returns NULL (that's just the project allocation conventions, okay), but lowkey the socket can still fail to connect or bind. And use the free function to release sockets made with any constructor β€” every single one.


πŸ”₯ CONNECTIONS πŸ”₯

To make an outgoing connection you use r_socket_connect or one of the convenience macros. There's a timeout parameter and a protocol selector so the same call surface works for TCP, UDP or Unix sockets. one API, many vibes ✨

Function signatures:

bool r_socket_connect (RSocket *s, const char *host, const char *port, int proto, unsigned int timeout);
bool r_socket_connect_tcp (RSocket *s, const char *host, const char *port, unsigned int timeout);
bool r_socket_connect_udp (RSocket *s, const char *host, const char *port, unsigned int timeout);
int r_socket_connect_serial (RSocket *sock, const char *path, int speed, int parity);

Return value: these return true on success and false on failure, periodt. Timeouts are in milliseconds for connect. And if you wanna check socket state after connecting, use r_socket_is_connected () and r_socket_ready ().

When you're using SSL sockets created with r_socket_new (true), the TLS negotiation happens during r_socket_connect () (that's the client side) or r_socket_accept () (server side) if OpenSSL was enabled at build time. πŸŽ€


πŸ‘‘ SERVERS πŸ‘‘

To accept incoming connections: create an RSocket, call r_socket_listen to bind the port, then repeatedly accept clients. A server socket can be plain, or TLS-enabled by passing a certificate file path to r_socket_listen (). main character energy fr fr πŸ‘‘

Function signatures:

bool r_socket_listen (RSocket *s, const char *port, const char *certfile);
RSocket *r_socket_accept (RSocket *s);
RSocket *r_socket_accept_timeout (RSocket *s, unsigned int timeout);
bool r_socket_close (RSocket *s);

Typical loop: call r_socket_accept () (or the timeout variant) and handle the returned RSocket * for the client β€” then free it when you're done, don't leave it hanging 😀. r_socket_close closes the underlying listening descriptor for when the server should stop.


πŸ“ DATA TRANSFER πŸ“

These give you blocking and non-blocking reads/writes, plus convenience helpers for formatted output and line-oriented input. so many options, all of them useful πŸ“

Function signatures:

int r_socket_write (RSocket *s, const void *buf, int len);
int r_socket_puts (RSocket *s, char *buf);
void r_socket_printf (RSocket *s, const char *fmt, ...);
int r_socket_read (RSocket *s, ut8 *read, int len);
int r_socket_read_block (RSocket *s, unsigned char *buf, int len);
int r_socket_gets (RSocket *s, char *buf, int size);
int r_socket_flush (RSocket *s);
ut8 *r_socket_slurp (RSocket *s, int *len);
int r_socket_ready (RSocket *s, int secs, int usecs);
bool r_socket_is_connected (RSocket *s);

Notes, bestie, listen up:

  • r_socket_gets () reads until newline (or until the buffer is full) and returns the number of bytes read (or negative on error). Use it for simple line-based protocols β€” like, for example, debugging servers in tests.
  • r_socket_read_block () attempts to read the requested number of bytes; use it when an exact size is expected. Plain r_socket_read () reads up to the provided buffer length and may return sooner. yes, they're different, that's the point 😌
  • After sending a response on an accepted connection, call r_socket_flush () to make sure buffered data is written before closing the socket.
  • Use r_socket_ready () to poll for readability/writability without busy looping. your CPU will thank you 🫢

🌈 HTTP CLIENT 🌈

r_socket contains simple HTTP client helpers that several parts of radare2 use to fetch small resources (for example rtr and plugin download helpers). They're intentionally light-weight: they return an allocated buffer with the full response body β€” there's no streaming API β€” and they're convenient for short fetches.

Function signatures:

char *r_socket_http_get (const char *url, const char **headers, int *code, int *rlen);
char *r_socket_http_post (const char *url, const char **headers, const char *data, int *code, int *rlen);

Behaviour and ownership: the returned pointer is malloc'd by the callee and must be freed by the caller. code receives the HTTP status code (when it's not NULL) and rlen receives the length of the returned buffer. When you're fetching large resources, prefer other I/O hooks (for example libr/io) so you're not holding large blobs in memory. we love a memory-conscious queen πŸ’…


🧚 RAP PROTOCOL 🧚

The RAP protocol is a compact request/reply protocol used to expose file operations remotely β€” open/read/write/seek, the whole set. radare2 exposes both server and client helpers that wrap RSocket so callers can implement remote file access or forward commands across a socket. iconic honestly 🧚

Function signatures:

RSocketRapServer *r_socket_rap_server_new (bool is_ssl, const char *port);
RSocketRapServer *r_socket_rap_server_create (const char *pathname);
void r_socket_rap_server_free (RSocketRapServer *rap_s);
bool r_socket_rap_server_listen (RSocketRapServer *rap_s, const char *certfile);
RSocket *r_socket_rap_server_accept (RSocketRapServer *rap_s);
bool r_socket_rap_server_continue (RSocketRapServer *rap_s);

Typical usage: create the server with r_socket_rap_server_new (), call r_socket_rap_server_listen () and loop while r_socket_rap_server_continue () returns true. The RAP helpers let you implement remote access to files or expose r2 commands across a socket. slay ✨


πŸ’œ R2PIPE AND PROCESS PIPES πŸ’œ

r2pipe is a convenience layer built on top of the process pipe helpers in the socket module. It lets you spawn a radare2 process (or literally any local command) and send/receive text commands through a pipe. Under the hood it's using the r_socket_proc_* APIs.

Function signatures:

R2Pipe *r2pipe_open (const char *cmd);
char *r2pipe_cmd (R2Pipe *r2pipe, const char *str);
RSocketProc *r_socket_proc_open (char *const argv[]);
int r_socket_proc_close (RSocketProc *sp);
int r_socket_proc_read (RSocketProc *sp, unsigned char *buf, int len);
int r_socket_proc_gets (RSocketProc *sp, char *buf, int size);
int r_socket_proc_write (RSocketProc *sp, void *buf, int len);
void r_socket_proc_printf (RSocketProc *sp, const char *fmt, ...);

r2pipe_cmd() returns a malloc'd string that the caller must free β€” say it with me, free your strings 🫢. And use the r_socket_proc_* primitives when you need lower-level control over the child process I/O (like non-blocking reads or scripted interactions). iykyk


⭐ PROTOCOLS ⭐

The API exposes a small set of protocol constants to pick the transport used by r_socket_connect (). Most people will go TCP or UDP; unix domain sockets get used by local-only services, and the serial/CAN constants are there to support platform-specific backends.

Supported protocol constants:

  • R_SOCKET_PROTO_TCP β€” TCP connections
  • R_SOCKET_PROTO_UDP β€” UDP datagrams
  • R_SOCKET_PROTO_UNIX β€” Unix domain sockets
  • R_SOCKET_PROTO_SERIAL β€” Serial connections
  • R_SOCKET_PROTO_CAN β€” CAN bus (Linux only)

πŸŽ€ EXAMPLES πŸŽ€

These examples are drawn from real test code and from how core components use the socket APIs. They show off the common patterns: simple clients, TLS-capable servers, and HTTP handling with the r_socket_http_* helpers. let's get into it πŸ’–

Minimal TCP client

(pattern from test/unit/legacy_unit/socket/client.c)

#include <r_socket.h>

int main(void) {
    char buf[1024];
    RSocket *s = r_socket_new (false);
    if (r_socket_connect (s, "localhost", "9090", R_SOCKET_PROTO_TCP, 10000)) {
        while (r_socket_gets (s, buf, sizeof (buf)) >= 0) {
            eprintf ("((%s))0, buf);
        }
    }
    r_socket_free (s);
    return 0;
}

TLS server

accepts a client and responds with a simple HTTP page (adapted from test/unit/legacy_unit/socket/serverssl.c):

#include <r_socket.h>
#define PORT "4433"

int main (int argc, char **argv) {
    char buf[2048];
    RSocket *s, *cli;
    if (argc < 2) {
        eprintf ("Use %s <cert>0, argv[0]);
        return 1;
    }
    s = r_socket_new (true);
    if (!r_socket_listen (s, PORT, argv[1])) {
        eprintf ("Error, cant listen at port: %s0, PORT);
        return 1;
    }                                                                     0);
    while ((cli = r_socket_accept (s))) {              0r10erver test page
        r_socket_read (cli, (unsigned10erver:bEKRServer
        strcpy (buf, "HTTP/1.0 200 OK
        r_socket_write (cli, buf, strlen (buf));
        r_socket_flush (cli);
        r_socket_free (cli);
    }
    r_socket_free (s);
    return 0;
}

HTTP server loop and reply

(pattern used by libr/core HTTP handlers)

RSocket *server = r_socket_new (false);
if (r_socket_listen (server, "8080", NULL)) {
    for (;;) {
        RSocketHTTPRequest *req = r_socket_http_accept (server, NULL);
        if (!req) { break; }
        /* req->path, req->method and req->data are populated by the helper */
        r_socket_http_response (req, 200, "Hello World", 0, NULL);
        r_socket_http_free (req);
    }
}
/* remember to close/free the listening socket when done */

πŸ’« SEE ALSO πŸ’«

r_core(3), r_util(3)


and that's the whole page bestie, go build something with sockets πŸ’–

okay and one lil sign-off, because the source deserves its flowers: this page is from the Debian manual, dated September 21, 2025 β€” and the manual name up top is, obviously, R_SOCKET(3). mwah πŸ’…βœ¨

πŸ“¦ where this page comes from