2.77 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    freeaddrinfo(res);
 89
 90#else /* WITHOUT_GETADDRINFO */
 91   struct sockaddr_in host;
 92
 93    memset(&host, '\0', sizeof(host));
 94
 95    if (addr != NULL) {
 96        struct hostent *he;
 97
 98        if ((he = gethostbyname(addr)) != NULL)
 99            memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length);
100        else
101            goto end;
102    }
103
104    struct servent *se;
105
106    if ((se = getservbyname(serv, "tcp")) != NULL)
107        host.sin_port = se->s_port;
108    else
109        host.sin_port = htons(atoi(serv));
110
111    host.sin_family = AF_INET;
112
113    if ((rs = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
114        /* reuse addr */
115        int i = 1;
116        setsockopt(rs, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i));
117
118        if (bind(rs, (struct sockaddr *)&host, sizeof(host)) == -1) {
119            close(rs);
120            rs = -1;
121        }
122        else
123            listen(rs, SOMAXCONN);
124    }
125
126#endif  /* WITHOUT_GETADDRINFO */
127end:
128    return rs;
129}
130
131
132int xs_socket_accept(int rs)
133/* accepts an incoming connection */
134{
135    struct sockaddr_storage addr;
136    socklen_t l = sizeof(addr);
137
138    return accept(rs, (struct sockaddr *)&addr, &l);
139}
140
141
142int _xs_socket_peername(int s, char *buf, int buf_size)
143/* fill the buffer with the socket peername */
144{
145    struct sockaddr_storage addr;
146    socklen_t slen = sizeof(addr);
147    const char *p = NULL;
148
149    if (getpeername(s, (struct sockaddr *)&addr, &slen) != -1) {
150        if (addr.ss_family == AF_INET) {
151            struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
152
153            p = inet_ntop(AF_INET, &sa->sin_addr, buf, buf_size);
154        }
155        else
156        if (addr.ss_family == AF_INET6) {
157            struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&addr;
158
159            p = inet_ntop(AF_INET6, &sa->sin6_addr, buf, buf_size);
160        }
161    }
162
163    return p != NULL;
164}
165
166
167int xs_socket_connect(const char *addr, const char *serv)
168/* creates a client connection socket */
169{
170    int d = -1;
171
172#ifndef WITHOUT_GETADDRINFO
173    struct addrinfo *res;
174    struct addrinfo hints;
175
176    memset(&hints, '\0', sizeof(hints));
177
178    hints.ai_socktype   = SOCK_STREAM;
179    hints.ai_flags      = AI_ADDRCONFIG;
180
181    if (getaddrinfo(addr, serv, &hints, &res) == 0) {
182        struct addrinfo *r;
183
184        for (r = res; r != NULL; r = r->ai_next) {
185            d = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
186
187            if (d != -1) {
188                if (connect(d, r->ai_addr, r->ai_addrlen) == 0)
189                    break;
190
191                close(d);
192                d = -1;
193            }
194        }
195
196        freeaddrinfo(res);
197    }
198
199#else /* WITHOUT_GETADDRINFO */
200
201    /* traditional socket interface */
202    struct hostent *he;
203
204    if ((he = gethostbyname(addr)) != NULL) {
205        struct sockaddr_in host;
206
207        memset(&host, '\0', sizeof(host));
208
209        memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length);
210        host.sin_family = he->h_addrtype;
211
212        struct servent *se;
213
214        if ((se = getservbyname(serv, "tcp")) != NULL)
215            host.sin_port = se->s_port;
216        else
217            host.sin_port = htons(atoi(serv));
218
219        if ((d = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
220            if (connect(d, (struct sockaddr *)&host, sizeof(host)) == -1) {
221                close(d);
222                d = -1;
223            }
224        }
225    }
226
227#endif  /* WITHOUT_GETADDRINFO */
228
229    return d;
230}
231
232
233#ifdef _XS_H
234
235xs_str *xs_socket_peername(int s)
236/* returns the remote address as a string */
237{
238    char buf[2028];
239    xs_str *p = NULL;
240
241    if (_xs_socket_peername(s, buf, sizeof(buf)))
242        p = xs_str_new(buf);
243
244    return p;
245}
246
247#endif /* _XS_H */
248
249#endif /* XS_IMPLEMENTATION */
250
251#endif /* _XS_SOCKET_H */