main xs_socket.h
  1/* copyright (c) 2022 - 2026 grunfink et al. / MIT license */
  2
  3#ifndef _XS_SOCKET_H
  4
  5#define _XS_SOCKET_H
  6
  7#include "xs.h"
  8#include <errno.h>
  9#include <sys/time.h>
 10#include <sys/types.h>
 11
 12int xs_socket_timeout(int s, double rto, double sto);
 13int xs_socket_server(const char *addr, const char *serv);
 14int xs_socket_accept(int rs);
 15int _xs_socket_peername(int s, char *buf, int buf_size);
 16int xs_socket_connect(const char *addr, const char *serv);
 17
 18#ifdef _XS_H
 19xs_str *xs_socket_peername(int s);
 20#endif
 21
 22
 23#ifdef XS_IMPLEMENTATION
 24
 25#include <sys/socket.h>
 26#include <netdb.h>
 27#include <netinet/in.h>
 28#include <arpa/inet.h>
 29#include <string.h>
 30#include <stdlib.h>
 31#include <unistd.h>
 32
 33
 34int xs_socket_timeout(int s, double rto, double sto)
 35/* sets the socket timeout in seconds */
 36{
 37    struct timeval tv;
 38    int ret = 0;
 39
 40    if (rto > 0.0) {
 41        tv.tv_sec  = (int)rto;
 42        tv.tv_usec = (int)((rto - (double)(int)rto) * 1000000.0);
 43
 44        ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
 45    }
 46
 47    if (sto > 0.0) {
 48        tv.tv_sec  = (int)sto;
 49        tv.tv_usec = (int)((sto - (double)(int)sto) * 1000000.0);
 50
 51        ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
 52    }
 53
 54    return ret;
 55}
 56
 57
 58int xs_socket_server(const char *addr, const char *serv)
 59/* opens a server socket by service name (or port as string) */
 60{
 61    int rs = -1;
 62#ifndef WITHOUT_GETADDRINFO
 63    struct addrinfo *res;
 64    struct addrinfo hints;
 65    int status;
 66
 67    memset(&hints, '\0', sizeof(hints));
 68
 69    hints.ai_family     = AF_UNSPEC;
 70    hints.ai_socktype   = SOCK_STREAM;
 71
 72    if (getaddrinfo(addr, serv, &hints, &res) != 0) {
 73        goto end;
 74    }
 75
 76    rs = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
 77
 78    /* reuse addr */
 79    int i = 1;
 80    setsockopt(rs, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i));
 81
 82    status = bind(rs, res->ai_addr, res->ai_addrlen);
 83    if (status == -1) {
 84        fprintf(stderr, "Error binding with status %d\n", status);
 85        close(rs);
 86        rs = -1;
 87    }
 88    else {
 89
 90      listen(rs, SOMAXCONN);
 91    }
 92
 93    freeaddrinfo(res);
 94
 95#else /* WITHOUT_GETADDRINFO */
 96   struct sockaddr_in host;
 97
 98    memset(&host, '\0', sizeof(host));
 99
100    if (addr != NULL) {
101        struct hostent *he;
102
103        if ((he = gethostbyname(addr)) != NULL)
104            memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length);
105        else
106            goto end;
107    }
108
109    struct servent *se;
110
111    if ((se = getservbyname(serv, "tcp")) != NULL)
112        host.sin_port = se->s_port;
113    else
114        host.sin_port = htons(atoi(serv));
115
116    host.sin_family = AF_INET;
117
118    if ((rs = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
119        /* reuse addr */
120        int i = 1;
121        setsockopt(rs, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i));
122
123        if (bind(rs, (struct sockaddr *)&host, sizeof(host)) == -1) {
124            close(rs);
125            rs = -1;
126        }
127        else
128            listen(rs, SOMAXCONN);
129    }
130
131#endif  /* WITHOUT_GETADDRINFO */
132end:
133    return rs;
134}
135
136
137int xs_socket_accept(int rs)
138/* accepts an incoming connection */
139{
140    struct sockaddr_storage addr;
141    socklen_t l = sizeof(addr);
142
143    return accept(rs, (struct sockaddr *)&addr, &l);
144}
145
146
147int _xs_socket_peername(int s, char *buf, int buf_size)
148/* fill the buffer with the socket peername */
149{
150    struct sockaddr_storage addr;
151    socklen_t slen = sizeof(addr);
152    const char *p = NULL;
153
154    if (getpeername(s, (struct sockaddr *)&addr, &slen) != -1) {
155        if (addr.ss_family == AF_INET) {
156            struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
157
158            p = inet_ntop(AF_INET, &sa->sin_addr, buf, buf_size);
159        }
160        else
161        if (addr.ss_family == AF_INET6) {
162            struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&addr;
163
164            p = inet_ntop(AF_INET6, &sa->sin6_addr, buf, buf_size);
165        }
166    }
167
168    return p != NULL;
169}
170
171
172int xs_socket_connect(const char *addr, const char *serv)
173/* creates a client connection socket */
174{
175    int d = -1;
176
177#ifndef WITHOUT_GETADDRINFO
178    struct addrinfo *res;
179    struct addrinfo hints;
180
181    memset(&hints, '\0', sizeof(hints));
182
183    hints.ai_socktype   = SOCK_STREAM;
184    hints.ai_flags      = AI_ADDRCONFIG;
185
186    if (getaddrinfo(addr, serv, &hints, &res) == 0) {
187        struct addrinfo *r;
188
189        for (r = res; r != NULL; r = r->ai_next) {
190            d = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
191
192            if (d != -1) {
193                if (connect(d, r->ai_addr, r->ai_addrlen) == 0)
194                    break;
195
196                close(d);
197                d = -1;
198            }
199        }
200
201        freeaddrinfo(res);
202    }
203
204#else /* WITHOUT_GETADDRINFO */
205
206    /* traditional socket interface */
207    struct hostent *he;
208
209    if ((he = gethostbyname(addr)) != NULL) {
210        struct sockaddr_in host;
211
212        memset(&host, '\0', sizeof(host));
213
214        memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length);
215        host.sin_family = he->h_addrtype;
216
217        struct servent *se;
218
219        if ((se = getservbyname(serv, "tcp")) != NULL)
220            host.sin_port = se->s_port;
221        else
222            host.sin_port = htons(atoi(serv));
223
224        if ((d = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
225            if (connect(d, (struct sockaddr *)&host, sizeof(host)) == -1) {
226                close(d);
227                d = -1;
228            }
229        }
230    }
231
232#endif  /* WITHOUT_GETADDRINFO */
233
234    return d;
235}
236
237
238#ifdef _XS_H
239
240xs_str *xs_socket_peername(int s)
241/* returns the remote address as a string */
242{
243    char buf[2028];
244    xs_str *p = NULL;
245
246    if (_xs_socket_peername(s, buf, sizeof(buf)))
247        p = xs_str_new(buf);
248
249    return p;
250}
251
252#endif /* _XS_H */
253
254#endif /* XS_IMPLEMENTATION */
255
256#endif /* _XS_SOCKET_H */