git ssb

0+

cel / sslh



Tree: 5cd1fa18753c479f7eaef26b893016a37646364f

Files: 5cd1fa18753c479f7eaef26b893016a37646364f / common.c

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

Built with git-ssb-web