git ssb

0+

cel / sslh



Tree: 9bcb2cdd7a920ebc78b59d0b5797d678424aa93a

Files: 9bcb2cdd7a920ebc78b59d0b5797d678424aa93a / common.c

14010 bytesRaw
1/* Code and variables that is common to both fork and select-based
2 * servers.
3 *
4 * No code here should assume whether sockets are blocking or not.
5 **/
6
7#define _GNU_SOURCE
8#include <stdarg.h>
9
10#include "common.h"
11
12/* Added to make the code compilable under CYGWIN
13 * */
14#ifndef SA_NOCLDWAIT
15#define SA_NOCLDWAIT 0
16#endif
17
18/*
19 * Settings that depend on the command line. They're set in main(), but also
20 * used in other places in common.c, and it'd be heavy-handed to pass it all as
21 * parameters
22 */
23int verbose = 0;
24int probing_timeout = 2;
25int inetd = 0;
26int foreground = 0;
27int numeric = 0;
28const char *user_name, *pid_file, *rule_filename;
29
30struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
31
32#ifdef LIBWRAP
33#include <tcpd.h>
34int allow_severity =0, deny_severity = 0;
35#endif
36
37
38/* check result and die, printing the offending address and error */
39void check_res_dumpdie(int res, struct addrinfo *addr, char* syscall)
40{
41 char buf[NI_MAXHOST];
42
43 if (res == -1) {
44 fprintf(stderr, "%s:%s: %s\n",
45 sprintaddr(buf, sizeof(buf), addr),
46 syscall,
47 strerror(errno));
48 exit(1);
49 }
50}
51
52/* Starts listening sockets on specified addresses.
53 * IN: addr[], num_addr
54 * OUT: *sockfd[] pointer to newly-allocated array of file descriptors
55 * Returns number of addresses bound
56 * Bound file descriptors are returned in newly-allocated *sockfd pointer
57 */
58int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list)
59{
60 struct sockaddr_storage *saddr;
61 struct addrinfo *addr;
62 int i, res, reuse;
63 int num_addr = 0;
64
65 for (addr = addr_list; addr; addr = addr->ai_next)
66 num_addr++;
67
68 if (verbose)
69 fprintf(stderr, "listening to %d addresses\n", num_addr);
70
71 *sockfd = malloc(num_addr * sizeof(*sockfd[0]));
72
73 for (i = 0, addr = addr_list; i < num_addr && addr; i++, addr = addr->ai_next) {
74 if (!addr) {
75 fprintf(stderr, "FATAL: Inconsistent listen number. This should not happen.\n");
76 exit(1);
77 }
78 saddr = (struct sockaddr_storage*)addr->ai_addr;
79
80 (*sockfd)[i] = socket(saddr->ss_family, SOCK_STREAM, 0);
81 check_res_dumpdie((*sockfd)[i], addr, "socket");
82
83 reuse = 1;
84 res = setsockopt((*sockfd)[i], SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
85 check_res_dumpdie(res, addr, "setsockopt");
86
87 res = bind((*sockfd)[i], addr->ai_addr, addr->ai_addrlen);
88 check_res_dumpdie(res, addr, "bind");
89
90 res = listen ((*sockfd)[i], 50);
91 check_res_dumpdie(res, addr, "listen");
92
93 }
94
95 return num_addr;
96}
97
98/* Connect to first address that works and returns a file descriptor, or -1 if
99 * none work. cnx_name points to the name of the service (for logging) */
100int connect_addr(struct addrinfo *addr, const char* cnx_name)
101{
102 struct addrinfo *a;
103 char buf[NI_MAXHOST];
104 int fd, res;
105
106 for (a = addr; a; a = a->ai_next) {
107 if (verbose)
108 fprintf(stderr, "connecting to %s family %d len %d\n",
109 sprintaddr(buf, sizeof(buf), a),
110 a->ai_addr->sa_family, a->ai_addrlen);
111 fd = socket(a->ai_family, SOCK_STREAM, 0);
112 if (fd == -1) {
113 log_message(LOG_ERR, "forward to %s failed:socket: %s\n", cnx_name, strerror(errno));
114 } else {
115 res = connect(fd, a->ai_addr, a->ai_addrlen);
116 if (res == -1) {
117 log_message(LOG_ERR, "forward to %s failed:connect: %s\n",
118 cnx_name, strerror(errno));
119 } else {
120 return fd;
121 }
122 }
123 }
124 return -1;
125}
126
127/* Store some data to write to the queue later */
128int defer_write(struct queue *q, void* data, int data_size)
129{
130 if (verbose)
131 fprintf(stderr, "**** writing defered on fd %d\n", q->fd);
132 q->defered_data = malloc(data_size);
133 q->begin_defered_data = q->defered_data;
134 q->defered_data_size = data_size;
135 memcpy(q->defered_data, data, data_size);
136
137 return 0;
138}
139
140/* tries to flush some of the data for specified queue
141 * Upon success, the number of bytes written is returned.
142 * Upon failure, -1 returned (e.g. connexion closed)
143 * */
144int flush_defered(struct queue *q)
145{
146 int n;
147
148 if (verbose)
149 fprintf(stderr, "flushing defered data to fd %d\n", q->fd);
150
151 n = write(q->fd, q->defered_data, q->defered_data_size);
152 if (n == -1)
153 return n;
154
155 if (n == q->defered_data_size) {
156 /* All has been written -- release the memory */
157 free(q->begin_defered_data);
158 q->begin_defered_data = NULL;
159 q->defered_data = NULL;
160 q->defered_data_size = 0;
161 } else {
162 /* There is data left */
163 q->defered_data += n;
164 q->defered_data_size -= n;
165 }
166
167
168 return n;
169}
170
171
172void init_cnx(struct connection *cnx)
173{
174 memset(cnx, 0, sizeof(*cnx));
175 cnx->q[0].fd = -1;
176 cnx->q[1].fd = -1;
177}
178
179void dump_connection(struct connection *cnx)
180{
181 printf("state: %d\n", cnx->state);
182 printf("fd %d, %d defered\n", cnx->q[0].fd, cnx->q[0].defered_data_size);
183 printf("fd %d, %d defered\n", cnx->q[1].fd, cnx->q[1].defered_data_size);
184}
185
186
187/*
188 * moves data from one fd to other
189 *
190 * retuns number of bytes copied if success
191 * returns 0 (FD_CNXCLOSED) if incoming socket closed
192 * returns FD_NODATA if no data was available
193 * returns FD_STALLED if data was read, could not be written, and has been
194 * stored in temporary buffer.
195 *
196 * slot for debug only and may go away at some point
197 */
198int fd2fd(struct queue *target_q, struct queue *from_q)
199{
200 char buffer[BUFSIZ];
201 int target, from, size_r, size_w;
202
203 target = target_q->fd;
204 from = from_q->fd;
205
206 size_r = read(from, buffer, sizeof(buffer));
207 if (size_r == -1) {
208 switch (errno) {
209 case EAGAIN:
210 if (verbose)
211 fprintf(stderr, "reading 0 from %d\n", from);
212 return FD_NODATA;
213
214 case ECONNRESET:
215 case EPIPE:
216 return FD_CNXCLOSED;
217 }
218 }
219
220 CHECK_RES_RETURN(size_r, "read");
221
222 if (size_r == 0)
223 return FD_CNXCLOSED;
224
225 size_w = write(target, buffer, size_r);
226 /* process -1 when we know how to deal with it */
227 if ((size_w == -1)) {
228 switch (errno) {
229 case EAGAIN:
230 /* write blocked: Defer data */
231 defer_write(target_q, buffer, size_r);
232 return FD_STALLED;
233
234 case ECONNRESET:
235 case EPIPE:
236 /* remove end closed -- drop the connection */
237 return FD_CNXCLOSED;
238 }
239 } else if (size_w < size_r) {
240 /* incomplete write -- defer the rest of the data */
241 defer_write(target_q, buffer + size_w, size_r - size_w);
242 return FD_STALLED;
243 }
244
245 CHECK_RES_RETURN(size_w, "write");
246
247 return size_w;
248}
249
250/* returns a string that prints the IP and port of the sockaddr */
251char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
252{
253 char host[NI_MAXHOST], serv[NI_MAXSERV];
254 int res;
255
256 res = getnameinfo(a->ai_addr, a->ai_addrlen,
257 host, sizeof(host),
258 serv, sizeof(serv),
259 numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 );
260
261 if (res) {
262 log_message(LOG_ERR, "sprintaddr:getnameinfo: %s\n", gai_strerror(res));
263 /* Name resolution failed: do it numerically instead */
264 res = getnameinfo(a->ai_addr, a->ai_addrlen,
265 host, sizeof(host),
266 serv, sizeof(serv),
267 NI_NUMERICHOST | NI_NUMERICSERV);
268 /* should not fail but... */
269 if (res) {
270 log_message(LOG_ERR, "sprintaddr:getnameinfo(NUM): %s\n", gai_strerror(res));
271 strcpy(host, "?");
272 strcpy(serv, "?");
273 }
274 }
275
276 snprintf(buf, size, "%s:%s", host, serv);
277
278 return buf;
279}
280
281/* Turns a hostname and port (or service) into a list of struct addrinfo
282 * returns 0 on success, -1 otherwise and logs error
283 **/
284int resolve_split_name(struct addrinfo **out, const char* host, const char* serv)
285{
286 struct addrinfo hint;
287 int res;
288
289 memset(&hint, 0, sizeof(hint));
290 hint.ai_family = PF_UNSPEC;
291 hint.ai_socktype = SOCK_STREAM;
292
293 res = getaddrinfo(host, serv, &hint, out);
294 if (res)
295 log_message(LOG_ERR, "%s `%s:%s'\n", gai_strerror(res), host, serv);
296 return res;
297}
298
299/* turns a "hostname:port" string into a list of struct addrinfo;
300out: list of newly allocated addrinfo (see getaddrinfo(3)); freeaddrinfo(3) when done
301fullname: input string -- it gets clobbered
302*/
303void resolve_name(struct addrinfo **out, char* fullname)
304{
305 char *serv, *host;
306 int res;
307
308 char *sep = strrchr(fullname, ':');
309
310 if (!sep) /* No separator: parameter is just a port */
311 {
312 fprintf(stderr, "%s: names must be fully specified as hostname:port\n", fullname);
313 exit(1);
314 }
315
316 host = fullname;
317 serv = sep+1;
318 *sep = 0;
319
320 res = resolve_split_name(out, host, serv);
321 if (res) {
322 fprintf(stderr, "%s `%s'\n", gai_strerror(res), fullname);
323 if (res == EAI_SERVICE)
324 fprintf(stderr, "(Check you have specified all ports)\n");
325 exit(4);
326 }
327}
328
329/* Log to syslog or stderr if foreground */
330void log_message(int type, char* msg, ...)
331{
332 va_list ap;
333
334 va_start(ap, msg);
335 if (foreground)
336 vfprintf(stderr, msg, ap);
337 else
338 vsyslog(type, msg, ap);
339 va_end(ap);
340}
341
342/* syslogs who connected to where */
343void log_connection(struct connection *cnx)
344{
345 struct addrinfo addr;
346 struct sockaddr_storage ss;
347#define MAX_NAMELENGTH (NI_MAXHOST + NI_MAXSERV + 1)
348 char peer[MAX_NAMELENGTH], service[MAX_NAMELENGTH],
349 local[MAX_NAMELENGTH], target[MAX_NAMELENGTH];
350 int res;
351
352 addr.ai_addr = (struct sockaddr*)&ss;
353 addr.ai_addrlen = sizeof(ss);
354
355 res = getpeername(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen);
356 if (res == -1) return; /* that should never happen, right? */
357 sprintaddr(peer, sizeof(peer), &addr);
358
359 addr.ai_addrlen = sizeof(ss);
360 res = getsockname(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen);
361 if (res == -1) return;
362 sprintaddr(service, sizeof(service), &addr);
363
364 addr.ai_addrlen = sizeof(ss);
365 res = getpeername(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen);
366 if (res == -1) return;
367 sprintaddr(target, sizeof(target), &addr);
368
369 addr.ai_addrlen = sizeof(ss);
370 res = getsockname(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen);
371 if (res == -1) return;
372 sprintaddr(local, sizeof(local), &addr);
373
374 log_message(LOG_INFO, "connection from %s to %s forwarded from %s to %s\n",
375 peer,
376 service,
377 local,
378 target);
379}
380
381
382/* libwrap (tcpd): check the connection is legal. This is necessary because
383 * the actual server will only see a connection coming from localhost and can't
384 * apply the rules itself.
385 *
386 * Returns -1 if access is denied, 0 otherwise
387 */
388int check_access_rights(int in_socket, const char* service)
389{
390#ifdef LIBWRAP
391 struct sockaddr peeraddr;
392 socklen_t size = sizeof(peeraddr);
393 char addr_str[NI_MAXHOST], host[NI_MAXHOST];
394 int res;
395
396 res = getpeername(in_socket, &peeraddr, &size);
397 CHECK_RES_DIE(res, "getpeername");
398
399 /* extract peer address */
400 res = getnameinfo(&peeraddr, size, addr_str, sizeof(addr_str), NULL, 0, NI_NUMERICHOST);
401 if (res) {
402 if (verbose)
403 fprintf(stderr, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res));
404 strcpy(addr_str, STRING_UNKNOWN);
405 }
406 /* extract peer name */
407 strcpy(host, STRING_UNKNOWN);
408 if (!numeric) {
409 res = getnameinfo(&peeraddr, size, host, sizeof(host), NULL, 0, NI_NAMEREQD);
410 if (res) {
411 if (verbose)
412 fprintf(stderr, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res));
413 }
414 }
415
416 if (!hosts_ctl(service, host, addr_str, STRING_UNKNOWN)) {
417 if (verbose)
418 fprintf(stderr, "access denied\n");
419 log_message(LOG_INFO, "connection from %s(%s): access denied", host, addr_str);
420 close(in_socket);
421 return -1;
422 }
423#endif
424 return 0;
425}
426
427void setup_signals(void)
428{
429 int res;
430 struct sigaction action;
431
432 /* Request no SIGCHLD is sent upon termination of
433 * the children */
434 memset(&action, 0, sizeof(action));
435 action.sa_handler = NULL;
436 action.sa_flags = SA_NOCLDWAIT;
437 res = sigaction(SIGCHLD, &action, NULL);
438 CHECK_RES_DIE(res, "sigaction");
439
440 /* Set SIGTERM to exit. For some reason if it's not set explicitely,
441 * coverage information is lost when killing the process */
442 memset(&action, 0, sizeof(action));
443 action.sa_handler = exit;
444 res = sigaction(SIGTERM, &action, NULL);
445 CHECK_RES_DIE(res, "sigaction");
446
447 /* Ignore SIGPIPE . */
448 action.sa_handler = SIG_IGN;
449 res = sigaction(SIGPIPE, &action, NULL);
450 CHECK_RES_DIE(res, "sigaction");
451
452}
453
454/* Open syslog connection with appropriate banner;
455 * banner is made up of basename(bin_name)+"[pid]" */
456void setup_syslog(const char* bin_name) {
457 char *name1, *name2;
458
459 name1 = strdup(bin_name);
460 asprintf(&name2, "%s[%d]", basename(name1), getpid());
461 openlog(name2, LOG_CONS, LOG_AUTH);
462 free(name1);
463 /* Don't free name2, as openlog(3) uses it (at least in glibc) */
464
465 log_message(LOG_INFO, "%s %s started\n", server_type, VERSION);
466}
467
468/* We don't want to run as root -- drop priviledges if required */
469void drop_privileges(const char* user_name)
470{
471 int res;
472 struct passwd *pw = getpwnam(user_name);
473 if (!pw) {
474 fprintf(stderr, "%s: not found\n", user_name);
475 exit(2);
476 }
477 if (verbose)
478 fprintf(stderr, "turning into %s\n", user_name);
479
480 res = setgid(pw->pw_gid);
481 CHECK_RES_DIE(res, "setgid");
482 setuid(pw->pw_uid);
483 CHECK_RES_DIE(res, "setuid");
484}
485
486/* Writes my PID */
487void write_pid_file(const char* pidfile)
488{
489 FILE *f;
490
491 f = fopen(pidfile, "w");
492 if (!f) {
493 perror(pidfile);
494 exit(3);
495 }
496
497 fprintf(f, "%d\n", getpid());
498 fclose(f);
499}
500
501

Built with git-ssb-web