2.54 xs_socket.h
  1/* copyright (c) 2022 - 2024 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);
  9FILE *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    struct sockaddr_in host;
 58
 59    memset(&host, '\0', sizeof(host));
 60
 61    if (addr != NULL) {
 62        struct hostent *he;
 63
 64        if ((he = gethostbyname(addr)) != NULL)
 65            memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length);
 66        else
 67            goto end;
 68    }
 69
 70    struct servent *se;
 71
 72    if ((se = getservbyname(serv, "tcp")) != NULL)
 73        host.sin_port = se->s_port;
 74    else
 75        host.sin_port = htons(atoi(serv));
 76
 77    host.sin_family = AF_INET;
 78
 79    if ((rs = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
 80        /* reuse addr */
 81        int i = 1;
 82        setsockopt(rs, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i));
 83
 84        if (bind(rs, (struct sockaddr *)&host, sizeof(host)) == -1) {
 85            close(rs);
 86            rs = -1;
 87        }
 88        else
 89            listen(rs, SOMAXCONN);
 90    }
 91
 92end:
 93    return rs;
 94}
 95
 96
 97FILE *xs_socket_accept(int rs)
 98/* accepts an incoming connection */
 99{
100    int cs = -1;
101    struct sockaddr_storage addr;
102    socklen_t l = sizeof(addr);
103
104    cs = accept(rs, (struct sockaddr *)&addr, &l);
105
106    return cs == -1 ? NULL : fdopen(cs, "r+");
107}
108
109
110int _xs_socket_peername(int s, char *buf, int buf_size)
111/* fill the buffer with the socket peername */
112{
113    struct sockaddr_storage addr;
114    socklen_t slen = sizeof(addr);
115    const char *p = NULL;
116
117    if (getpeername(s, (struct sockaddr *)&addr, &slen) != -1) {
118        if (addr.ss_family == AF_INET) {
119            struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
120
121            p = inet_ntop(AF_INET, &sa->sin_addr, buf, buf_size);
122        }
123        else
124        if (addr.ss_family == AF_INET6) {
125            struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&addr;
126
127            p = inet_ntop(AF_INET6, &sa->sin6_addr, buf, buf_size);
128        }
129    }
130
131    return p != NULL;
132}
133
134
135int xs_socket_connect(const char *addr, const char *serv)
136/* creates a client connection socket */
137{
138    int d = -1;
139
140#ifndef WITHOUT_GETADDRINFO
141    struct addrinfo *res;
142    struct addrinfo hints;
143
144    memset(&hints, '\0', sizeof(hints));
145
146    hints.ai_socktype   = SOCK_STREAM;
147    hints.ai_flags      = AI_ADDRCONFIG;
148
149    if (getaddrinfo(addr, serv, &hints, &res) == 0) {
150        struct addrinfo *r;
151
152        for (r = res; r != NULL; r = r->ai_next) {
153            d = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
154
155            if (d != -1) {
156                if (connect(d, r->ai_addr, r->ai_addrlen) == 0)
157                    break;
158
159                close(d);
160                d = -1;
161            }
162        }
163
164        freeaddrinfo(res);
165    }
166
167#else /* WITHOUT_GETADDRINFO */
168
169    /* traditional socket interface */
170    struct hostent *he;
171
172    if ((he = gethostbyname(addr)) != NULL) {
173        struct sockaddr_in host;
174
175        memset(&host, '\0', sizeof(host));
176
177        memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length);
178        host.sin_family = he->h_addrtype;
179
180        struct servent *se;
181
182        if ((se = getservbyname(serv, "tcp")) != NULL)
183            host.sin_port = se->s_port;
184        else
185            host.sin_port = htons(atoi(serv));
186
187        if ((d = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
188            if (connect(d, (struct sockaddr *)&host, sizeof(host)) == -1)
189                d = -1;
190        }
191    }
192
193#endif  /* WITHOUT_GETADDRINFO */
194
195    return d;
196}
197
198
199#ifdef _XS_H
200
201xs_str *xs_socket_peername(int s)
202/* returns the remote address as a string */
203{
204    char buf[2028];
205    xs_str *p = NULL;
206
207    if (_xs_socket_peername(s, buf, sizeof(buf)))
208        p = xs_str_new(buf);
209
210    return p;
211}
212
213#endif /* _XS_H */
214
215#endif /* XS_IMPLEMENTATION */
216
217#endif /* _XS_SOCKET_H */