socket-util.c

Socket helper utils
git clone git://git.finwo.net/lib/socket-util.c
Log | Files | Refs | README | LICENSE

README.md (4168B)


      1 # socket-util
      2 
      3 A C library for common socket utilities including TCP/UDP/Unix socket creation and sockaddr manipulation.
      4 
      5 ## Installation
      6 
      7 This library can be installed using the [dep](https://github.com/finwo/dep) package manager:
      8 
      9 ```bash
     10 dep add finwo/socket-util
     11 ```
     12 
     13 ## Usage
     14 
     15 ```c
     16 #include "socket-util.h"
     17 
     18 int *fds = tcp_listen(":8080", NULL, NULL);
     19 if (fds) {
     20     // fds[0] = count, fds[1+] = socket fds
     21     for (int i = 1; i <= fds[0]; i++) {
     22         printf("Listening on fd %d\n", fds[i]);
     23     }
     24     free(fds);
     25 }
     26 ```
     27 
     28 ## Design Pattern: Config with Defaults
     29 
     30 The `tcp_listen` and `udp_recv` functions are designed to read addresses from config files while providing programmatic fallback defaults:
     31 
     32 ```c
     33 int *fds = tcp_listen(config_address, default_host, default_port);
     34 ```
     35 
     36 - **First argument** (`addr`): Read from config file - can be empty/NULL
     37 - **Second argument** (`default_host`): Hardcoded fallback host
     38 - **Third argument** (`default_port`): Hardcoded fallback port
     39 
     40 This allows flexible configuration:
     41 
     42 ```c
     43 // Config provides ":8080" -> binds to 0.0.0.0:8080
     44 int *fds = tcp_listen(":8080", NULL, NULL);
     45 
     46 // Config provides "" (empty) -> uses hardcoded defaults
     47 int *fds = tcp_listen("", "192.168.1.100", "5060");
     48 
     49 // Config provides ":0" -> binds to random port, caller must detect actual port
     50 int *fds = tcp_listen(":0", NULL, "0");
     51 ```
     52 
     53 ## Features
     54 
     55 ### TCP Listening
     56 
     57 ```c
     58 // Simple: port from config, no defaults
     59 int *fds = tcp_listen(":8080", NULL, NULL);
     60 
     61 // Config-driven with defaults
     62 int *fds = tcp_listen(config->sip_host, "0.0.0.0", "5060");
     63 
     64 // Dual-stack (IPv4 + IPv6)
     65 int *fds = tcp_listen(":5060", NULL, NULL);
     66 
     67 // IPv4 only
     68 int *fds = tcp_listen("0.0.0.0:5060", NULL, NULL);
     69 
     70 // IPv6 only
     71 int *fds = tcp_listen("[::]:5060", NULL, NULL);
     72 ```
     73 
     74 ### UDP Receiving
     75 
     76 ```c
     77 // Simple: port from config
     78 int *fds = udp_recv(":5060", NULL, NULL);
     79 
     80 // With defaults
     81 int *fds = udp_recv(config->rtp_host, "0.0.0.0", "10000");
     82 ```
     83 
     84 ### Unix Domain Sockets
     85 
     86 ```c
     87 // Stream socket
     88 int *fds = unix_listen("/run/mydaemon.sock", SOCK_STREAM, NULL);
     89 
     90 // With ownership
     91 int *fds = unix_listen("/run/mydaemon.sock", SOCK_STREAM, "daemonuser");
     92 
     93 // Datagram socket with group
     94 int *fds = unix_listen("/run/mydaemon.sock", SOCK_DGRAM, "daemonuser:daemogroup");
     95 ```
     96 
     97 ### Socket Address Conversion
     98 
     99 ```c
    100 struct sockaddr_in addr;
    101 addr.sin_family = AF_INET;
    102 addr.sin_port = htons(8080);
    103 inet_pton(AF_INET, "192.168.1.1", &addr.sin_addr);
    104 
    105 char buf[INET6_ADDRSTRLEN + 8];
    106 sockaddr_to_string((struct sockaddr *)&addr, buf, sizeof(buf));
    107 // buf = "192.168.1.1:8080"
    108 ```
    109 
    110 ### String to Socket Address
    111 
    112 ```c
    113 struct sockaddr_storage addr;
    114 int result = string_to_sockaddr("192.168.1.1:8080", &addr);
    115 // result = 0 on success, -1 on error
    116 
    117 result = string_to_sockaddr("[::1]:3000", &addr);
    118 // For IPv6, wrap address in brackets
    119 ```
    120 
    121 ### Compare Socket Addresses
    122 
    123 ```c
    124 struct sockaddr_in a, b;
    125 a.sin_family = AF_INET;
    126 b.sin_family = AF_INET;
    127 // ... set addresses ...
    128 
    129 if (sockaddr_equal((struct sockaddr *)&a, (struct sockaddr *)&b)) {
    130     // Addresses are equal
    131 }
    132 ```
    133 
    134 ### Merge File Descriptor Arrays
    135 
    136 ```c
    137 int *arr1 = malloc(sizeof(int) * 3);
    138 arr1[0] = 2;
    139 arr1[1] = 5;
    140 arr1[2] = 10;
    141 
    142 int *arr2 = malloc(sizeof(int) * 2);
    143 arr2[0] = 1;
    144 arr2[1] = 3;
    145 
    146 int *arrays[] = { arr1, arr2 };
    147 int *merged = merge_fd_arrays(arrays, 2);
    148 // merged[0] = 3, merged[1] = 5, merged[2] = 10, merged[3] = 3
    149 // Note: original arrays are freed
    150 ```
    151 
    152 ### Set Non-blocking
    153 
    154 ```c
    155 int fd = socket(AF_INET, SOCK_STREAM, 0);
    156 set_socket_nonblocking(fd, 1);  // Set non-blocking
    157 set_socket_nonblocking(fd, 0);  // Set blocking
    158 ```
    159 
    160 ## Address Format
    161 
    162 The address parsing supports:
    163 
    164 - Port only: `:8080`, `8080` (uses defaults for host)
    165 - IPv4: `192.168.1.1:8080`
    166 - IPv6: `[::1]:8080`, `[2001:db8::1]:443`
    167 - With defaults: `8080` with default host `0.0.0.0`
    168 
    169 ## Return Value Conventions
    170 
    171 Functions returning `int *` (arrays):
    172 - Returns `NULL` on error
    173 - Index 0 contains the count
    174 - Indices 1+ contain the values
    175 - Caller is responsible for freeing the array
    176 
    177 Functions returning `int`:
    178 - Returns 0 on success
    179 - Returns -1 on error
    180 
    181 ## License
    182 
    183 Copyright (c) 2026 finwo. See LICENSE.md for details.