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