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