2.69
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 */