1 |
|
|
/* $OpenBSD: tftp-proxy.c,v 1.21 2017/07/04 12:47:51 florian Exp $ |
2 |
|
|
* |
3 |
|
|
* Copyright (c) 2005 DLS Internet Services |
4 |
|
|
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl> |
5 |
|
|
* |
6 |
|
|
* Redistribution and use in source and binary forms, with or without |
7 |
|
|
* modification, are permitted provided that the following conditions |
8 |
|
|
* are met: |
9 |
|
|
* |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* 3. The name of the author may not be used to endorse or promote products |
16 |
|
|
* derived from this software without specific prior written permission. |
17 |
|
|
* |
18 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
19 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
20 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
21 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
22 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
23 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
27 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 |
|
|
*/ |
29 |
|
|
|
30 |
|
|
#include <sys/types.h> |
31 |
|
|
#include <sys/ioctl.h> |
32 |
|
|
#include <sys/socket.h> |
33 |
|
|
#include <sys/uio.h> |
34 |
|
|
|
35 |
|
|
#include <netinet/in.h> |
36 |
|
|
#include <arpa/inet.h> |
37 |
|
|
#include <arpa/tftp.h> |
38 |
|
|
#include <net/if.h> |
39 |
|
|
#include <net/pfvar.h> |
40 |
|
|
#include <netdb.h> |
41 |
|
|
|
42 |
|
|
#include <unistd.h> |
43 |
|
|
#include <errno.h> |
44 |
|
|
#include <err.h> |
45 |
|
|
#include <pwd.h> |
46 |
|
|
#include <stdio.h> |
47 |
|
|
#include <syslog.h> |
48 |
|
|
#include <string.h> |
49 |
|
|
#include <stdarg.h> |
50 |
|
|
#include <stdlib.h> |
51 |
|
|
#include <event.h> |
52 |
|
|
|
53 |
|
|
#include "filter.h" |
54 |
|
|
|
55 |
|
|
#define CHROOT_DIR "/var/empty" |
56 |
|
|
#define NOPRIV_USER "_tftp_proxy" |
57 |
|
|
|
58 |
|
|
#define DEFTRANSWAIT 2 |
59 |
|
|
#define NTOP_BUFS 4 |
60 |
|
|
#define PKTSIZE SEGSIZE+4 |
61 |
|
|
|
62 |
|
|
const char *opcode(int); |
63 |
|
|
const char *sock_ntop(struct sockaddr *); |
64 |
|
|
static void usage(void); |
65 |
|
|
|
66 |
|
|
struct proxy_listener { |
67 |
|
|
struct event ev; |
68 |
|
|
TAILQ_ENTRY(proxy_listener) entry; |
69 |
|
|
int (*cmsg2dst)(struct cmsghdr *, struct sockaddr_storage *); |
70 |
|
|
int s; |
71 |
|
|
}; |
72 |
|
|
|
73 |
|
|
void proxy_listen(const char *, const char *, int); |
74 |
|
|
void proxy_listener_events(void); |
75 |
|
|
int proxy_dst4(struct cmsghdr *, struct sockaddr_storage *); |
76 |
|
|
int proxy_dst6(struct cmsghdr *, struct sockaddr_storage *); |
77 |
|
|
void proxy_recv(int, short, void *); |
78 |
|
|
|
79 |
|
|
struct fd_reply { |
80 |
|
|
TAILQ_ENTRY(fd_reply) entry; |
81 |
|
|
int fd; |
82 |
|
|
}; |
83 |
|
|
|
84 |
|
|
struct privproc { |
85 |
|
|
struct event pop_ev; |
86 |
|
|
struct event push_ev; |
87 |
|
|
TAILQ_HEAD(, fd_reply) replies; |
88 |
|
|
struct evbuffer *buf; |
89 |
|
|
}; |
90 |
|
|
|
91 |
|
|
void proxy_privproc(int, struct passwd *); |
92 |
|
|
void privproc_push(int, short, void *); |
93 |
|
|
void privproc_pop(int, short, void *); |
94 |
|
|
|
95 |
|
|
void unprivproc_push(int, short, void *); |
96 |
|
|
void unprivproc_pop(int, short, void *); |
97 |
|
|
void unprivproc_timeout(int, short, void *); |
98 |
|
|
|
99 |
|
|
char ntop_buf[NTOP_BUFS][INET6_ADDRSTRLEN]; |
100 |
|
|
|
101 |
|
|
struct loggers { |
102 |
|
|
__dead void (*err)(int, const char *, ...) |
103 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
104 |
|
|
__dead void (*errx)(int, const char *, ...) |
105 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
106 |
|
|
void (*warn)(const char *, ...) |
107 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
108 |
|
|
void (*warnx)(const char *, ...) |
109 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
110 |
|
|
void (*info)(const char *, ...) |
111 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
112 |
|
|
void (*debug)(const char *, ...) |
113 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
114 |
|
|
}; |
115 |
|
|
|
116 |
|
|
const struct loggers conslogger = { |
117 |
|
|
err, |
118 |
|
|
errx, |
119 |
|
|
warn, |
120 |
|
|
warnx, |
121 |
|
|
warnx, /* info */ |
122 |
|
|
warnx /* debug */ |
123 |
|
|
}; |
124 |
|
|
|
125 |
|
|
__dead void syslog_err(int, const char *, ...) |
126 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
127 |
|
|
__dead void syslog_errx(int, const char *, ...) |
128 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
129 |
|
|
void syslog_warn(const char *, ...) |
130 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
131 |
|
|
void syslog_warnx(const char *, ...) |
132 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
133 |
|
|
void syslog_info(const char *, ...) |
134 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
135 |
|
|
void syslog_debug(const char *, ...) |
136 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
137 |
|
|
void syslog_vstrerror(int, int, const char *, va_list) |
138 |
|
|
__attribute__((__format__ (printf, 3, 0))); |
139 |
|
|
|
140 |
|
|
const struct loggers syslogger = { |
141 |
|
|
syslog_err, |
142 |
|
|
syslog_errx, |
143 |
|
|
syslog_warn, |
144 |
|
|
syslog_warnx, |
145 |
|
|
syslog_info, |
146 |
|
|
syslog_debug |
147 |
|
|
}; |
148 |
|
|
|
149 |
|
|
const struct loggers *logger = &conslogger; |
150 |
|
|
|
151 |
|
|
#define lerr(_e, _f...) logger->err((_e), _f) |
152 |
|
|
#define lerrx(_e, _f...) logger->errx((_e), _f) |
153 |
|
|
#define lwarn(_f...) logger->warn(_f) |
154 |
|
|
#define lwarnx(_f...) logger->warnx(_f) |
155 |
|
|
#define linfo(_f...) logger->info(_f) |
156 |
|
|
#define ldebug(_f...) logger->debug(_f) |
157 |
|
|
|
158 |
|
|
__dead void |
159 |
|
|
usage(void) |
160 |
|
|
{ |
161 |
|
|
extern char *__progname; |
162 |
|
|
fprintf(stderr, "usage: %s [-46dv] [-a address] [-l address] [-p port]" |
163 |
|
|
" [-w transwait]\n", __progname); |
164 |
|
|
exit(1); |
165 |
|
|
} |
166 |
|
|
|
167 |
|
|
int debug = 0; |
168 |
|
|
int verbose = 0; |
169 |
|
|
struct timeval transwait = { DEFTRANSWAIT, 0 }; |
170 |
|
|
|
171 |
|
|
int on = 1; |
172 |
|
|
|
173 |
|
|
struct addr_pair { |
174 |
|
|
struct sockaddr_storage src; |
175 |
|
|
struct sockaddr_storage dst; |
176 |
|
|
}; |
177 |
|
|
|
178 |
|
|
struct proxy_request { |
179 |
|
|
char buf[SEGSIZE_MAX + 4]; |
180 |
|
|
size_t buflen; |
181 |
|
|
|
182 |
|
|
struct addr_pair addrs; |
183 |
|
|
|
184 |
|
|
struct event ev; |
185 |
|
|
TAILQ_ENTRY(proxy_request) entry; |
186 |
|
|
u_int32_t id; |
187 |
|
|
}; |
188 |
|
|
|
189 |
|
|
struct proxy_child { |
190 |
|
|
TAILQ_HEAD(, proxy_request) fdrequests; |
191 |
|
|
TAILQ_HEAD(, proxy_request) tmrequests; |
192 |
|
|
struct event push_ev; |
193 |
|
|
struct event pop_ev; |
194 |
|
|
struct evbuffer *buf; |
195 |
|
|
}; |
196 |
|
|
|
197 |
|
|
struct proxy_child *child = NULL; |
198 |
|
|
TAILQ_HEAD(, proxy_listener) proxy_listeners; |
199 |
|
|
|
200 |
|
|
struct src_addr { |
201 |
|
|
TAILQ_ENTRY(src_addr) entry; |
202 |
|
|
struct sockaddr_storage addr; |
203 |
|
|
socklen_t addrlen; |
204 |
|
|
}; |
205 |
|
|
TAILQ_HEAD(, src_addr) src_addrs; |
206 |
|
|
|
207 |
|
|
void source_addresses(const char*, int); |
208 |
|
|
|
209 |
|
|
int |
210 |
|
|
main(int argc, char *argv[]) |
211 |
|
|
{ |
212 |
|
|
extern char *__progname; |
213 |
|
|
|
214 |
|
|
int c; |
215 |
|
|
const char *errstr; |
216 |
|
|
|
217 |
|
|
struct src_addr *saddr, *saddr2; |
218 |
|
|
struct passwd *pw; |
219 |
|
|
|
220 |
|
|
char *addr = "localhost"; |
221 |
|
|
char *port = "6969"; |
222 |
|
|
int family = AF_UNSPEC; |
223 |
|
|
|
224 |
|
|
int pair[2]; |
225 |
|
|
|
226 |
|
|
TAILQ_INIT(&src_addrs); |
227 |
|
|
|
228 |
|
|
while ((c = getopt(argc, argv, "46a:dvl:p:w:")) != -1) { |
229 |
|
|
switch (c) { |
230 |
|
|
case '4': |
231 |
|
|
family = AF_INET; |
232 |
|
|
break; |
233 |
|
|
case '6': |
234 |
|
|
family = AF_INET6; |
235 |
|
|
break; |
236 |
|
|
case 'a': |
237 |
|
|
source_addresses(optarg, family); |
238 |
|
|
break; |
239 |
|
|
case 'd': |
240 |
|
|
verbose = debug = 1; |
241 |
|
|
break; |
242 |
|
|
case 'l': |
243 |
|
|
addr = optarg; |
244 |
|
|
break; |
245 |
|
|
case 'p': |
246 |
|
|
port = optarg; |
247 |
|
|
break; |
248 |
|
|
case 'v': |
249 |
|
|
verbose = 1; |
250 |
|
|
break; |
251 |
|
|
case 'w': |
252 |
|
|
transwait.tv_sec = strtonum(optarg, 1, 30, &errstr); |
253 |
|
|
if (errstr) |
254 |
|
|
errx(1, "wait is %s", errstr); |
255 |
|
|
break; |
256 |
|
|
default: |
257 |
|
|
usage(); |
258 |
|
|
/* NOTREACHED */ |
259 |
|
|
} |
260 |
|
|
} |
261 |
|
|
|
262 |
|
|
if (geteuid() != 0) |
263 |
|
|
lerrx(1, "need root privileges"); |
264 |
|
|
|
265 |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC, pair) |
266 |
|
|
== -1) |
267 |
|
|
lerr(1, "socketpair"); |
268 |
|
|
|
269 |
|
|
pw = getpwnam(NOPRIV_USER); |
270 |
|
|
if (pw == NULL) |
271 |
|
|
lerrx(1, "no %s user", NOPRIV_USER); |
272 |
|
|
|
273 |
|
|
/* Family option may have been specified late. */ |
274 |
|
|
if (family != AF_UNSPEC) |
275 |
|
|
TAILQ_FOREACH_SAFE(saddr, &src_addrs, entry, saddr2) |
276 |
|
|
if (saddr->addr.ss_family != family) { |
277 |
|
|
TAILQ_REMOVE(&src_addrs, saddr, entry); |
278 |
|
|
free(saddr); |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
if (!debug) { |
282 |
|
|
if (daemon(1, 0) == -1) |
283 |
|
|
lerr(1, "daemon"); |
284 |
|
|
|
285 |
|
|
openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); |
286 |
|
|
tzset(); |
287 |
|
|
logger = &syslogger; |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
switch (fork()) { |
291 |
|
|
case -1: |
292 |
|
|
lerr(1, "fork"); |
293 |
|
|
|
294 |
|
|
case 0: |
295 |
|
|
setproctitle("privproc"); |
296 |
|
|
close(pair[1]); |
297 |
|
|
proxy_privproc(pair[0], pw); |
298 |
|
|
/* this never returns */ |
299 |
|
|
|
300 |
|
|
default: |
301 |
|
|
setproctitle("unprivproc"); |
302 |
|
|
close(pair[0]); |
303 |
|
|
break; |
304 |
|
|
} |
305 |
|
|
|
306 |
|
|
child = calloc(1, sizeof(*child)); |
307 |
|
|
if (child == NULL) |
308 |
|
|
lerr(1, "alloc(child)"); |
309 |
|
|
|
310 |
|
|
child->buf = evbuffer_new(); |
311 |
|
|
if (child->buf == NULL) |
312 |
|
|
lerr(1, "child evbuffer"); |
313 |
|
|
|
314 |
|
|
TAILQ_INIT(&child->fdrequests); |
315 |
|
|
TAILQ_INIT(&child->tmrequests); |
316 |
|
|
|
317 |
|
|
proxy_listen(addr, port, family); |
318 |
|
|
|
319 |
|
|
/* open /dev/pf */ |
320 |
|
|
init_filter(NULL, verbose); |
321 |
|
|
|
322 |
|
|
/* revoke privs */ |
323 |
|
|
if (chroot(CHROOT_DIR) == -1) |
324 |
|
|
lerr(1, "chroot %s", CHROOT_DIR); |
325 |
|
|
|
326 |
|
|
if (chdir("/") == -1) |
327 |
|
|
lerr(1, "chdir %s", CHROOT_DIR); |
328 |
|
|
|
329 |
|
|
if (setgroups(1, &pw->pw_gid) || |
330 |
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || |
331 |
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) |
332 |
|
|
err(1, "unable to revoke privs"); |
333 |
|
|
|
334 |
|
|
event_init(); |
335 |
|
|
|
336 |
|
|
proxy_listener_events(); |
337 |
|
|
|
338 |
|
|
event_set(&child->pop_ev, pair[1], EV_READ | EV_PERSIST, |
339 |
|
|
unprivproc_pop, NULL); |
340 |
|
|
event_set(&child->push_ev, pair[1], EV_WRITE, |
341 |
|
|
unprivproc_push, NULL); |
342 |
|
|
|
343 |
|
|
event_add(&child->pop_ev, NULL); |
344 |
|
|
|
345 |
|
|
event_dispatch(); |
346 |
|
|
|
347 |
|
|
return(0); |
348 |
|
|
} |
349 |
|
|
|
350 |
|
|
void |
351 |
|
|
source_addresses(const char* name, int family) |
352 |
|
|
{ |
353 |
|
|
struct addrinfo hints, *res, *res0; |
354 |
|
|
struct src_addr *saddr; |
355 |
|
|
int error; |
356 |
|
|
|
357 |
|
|
memset(&hints, 0, sizeof(hints)); |
358 |
|
|
hints.ai_family = family; |
359 |
|
|
hints.ai_socktype = SOCK_DGRAM; |
360 |
|
|
hints.ai_flags = AI_PASSIVE; |
361 |
|
|
error = getaddrinfo(name, NULL, &hints, &res0); |
362 |
|
|
if (error) |
363 |
|
|
lerrx(1, "%s: %s", name, gai_strerror(error)); |
364 |
|
|
for (res = res0; res != NULL; res = res->ai_next) { |
365 |
|
|
if ((saddr = calloc(1, sizeof(struct src_addr))) == NULL) |
366 |
|
|
lerrx(1, "calloc"); |
367 |
|
|
memcpy(&(saddr->addr), res->ai_addr, res->ai_addrlen); |
368 |
|
|
saddr->addrlen = res->ai_addrlen; |
369 |
|
|
TAILQ_INSERT_TAIL(&src_addrs, saddr, entry); |
370 |
|
|
} |
371 |
|
|
freeaddrinfo(res0); |
372 |
|
|
} |
373 |
|
|
|
374 |
|
|
void |
375 |
|
|
proxy_privproc(int s, struct passwd *pw) |
376 |
|
|
{ |
377 |
|
|
struct privproc p; |
378 |
|
|
|
379 |
|
|
if (chroot(CHROOT_DIR) == -1) |
380 |
|
|
lerr(1, "chroot to %s", CHROOT_DIR); |
381 |
|
|
|
382 |
|
|
if (chdir("/") == -1) |
383 |
|
|
lerr(1, "chdir to %s", CHROOT_DIR); |
384 |
|
|
|
385 |
|
|
if (setgroups(1, &pw->pw_gid) || |
386 |
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid)) |
387 |
|
|
lerr(1, "unable to set group ids"); |
388 |
|
|
|
389 |
|
|
if (pledge("stdio inet sendfd flock rpath cpath wpath", NULL) == -1) |
390 |
|
|
err(1, "pledge"); |
391 |
|
|
|
392 |
|
|
TAILQ_INIT(&p.replies); |
393 |
|
|
|
394 |
|
|
p.buf = evbuffer_new(); |
395 |
|
|
if (p.buf == NULL) |
396 |
|
|
err(1, "pop evbuffer_new"); |
397 |
|
|
|
398 |
|
|
event_init(); |
399 |
|
|
|
400 |
|
|
event_set(&p.pop_ev, s, EV_READ | EV_PERSIST, privproc_pop, &p); |
401 |
|
|
event_set(&p.push_ev, s, EV_WRITE, privproc_push, &p); |
402 |
|
|
|
403 |
|
|
event_add(&p.pop_ev, NULL); |
404 |
|
|
|
405 |
|
|
event_dispatch(); |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
void |
409 |
|
|
privproc_pop(int fd, short events, void *arg) |
410 |
|
|
{ |
411 |
|
|
struct addr_pair req; |
412 |
|
|
struct privproc *p = arg; |
413 |
|
|
struct fd_reply *rep; |
414 |
|
|
struct src_addr *saddr; |
415 |
|
|
int add = 0; |
416 |
|
|
|
417 |
|
|
switch (evbuffer_read(p->buf, fd, sizeof(req))) { |
418 |
|
|
case 0: |
419 |
|
|
lerrx(1, "unprivproc has gone"); |
420 |
|
|
case -1: |
421 |
|
|
switch (errno) { |
422 |
|
|
case EAGAIN: |
423 |
|
|
case EINTR: |
424 |
|
|
return; |
425 |
|
|
default: |
426 |
|
|
lerr(1, "privproc_pop read"); |
427 |
|
|
} |
428 |
|
|
default: |
429 |
|
|
break; |
430 |
|
|
} |
431 |
|
|
|
432 |
|
|
while (EVBUFFER_LENGTH(p->buf) >= sizeof(req)) { |
433 |
|
|
evbuffer_remove(p->buf, &req, sizeof(req)); |
434 |
|
|
|
435 |
|
|
/* do i really need to check this? */ |
436 |
|
|
if (req.src.ss_family != req.dst.ss_family) |
437 |
|
|
lerrx(1, "family mismatch"); |
438 |
|
|
|
439 |
|
|
rep = calloc(1, sizeof(*rep)); |
440 |
|
|
if (rep == NULL) |
441 |
|
|
lerr(1, "reply calloc"); |
442 |
|
|
|
443 |
|
|
rep->fd = socket(req.src.ss_family, SOCK_DGRAM | SOCK_NONBLOCK, |
444 |
|
|
IPPROTO_UDP); |
445 |
|
|
if (rep->fd == -1) |
446 |
|
|
lerr(1, "privproc socket"); |
447 |
|
|
|
448 |
|
|
if (setsockopt(rep->fd, SOL_SOCKET, SO_BINDANY, |
449 |
|
|
&on, sizeof(on)) == -1) |
450 |
|
|
lerr(1, "privproc setsockopt(BINDANY)"); |
451 |
|
|
|
452 |
|
|
if (setsockopt(rep->fd, SOL_SOCKET, SO_REUSEADDR, |
453 |
|
|
&on, sizeof(on)) == -1) |
454 |
|
|
lerr(1, "privproc setsockopt(REUSEADDR)"); |
455 |
|
|
|
456 |
|
|
if (setsockopt(rep->fd, SOL_SOCKET, SO_REUSEPORT, |
457 |
|
|
&on, sizeof(on)) == -1) |
458 |
|
|
lerr(1, "privproc setsockopt(REUSEPORT)"); |
459 |
|
|
|
460 |
|
|
TAILQ_FOREACH(saddr, &src_addrs, entry) |
461 |
|
|
if (saddr->addr.ss_family == req.src.ss_family) |
462 |
|
|
break; |
463 |
|
|
if (saddr == NULL) { |
464 |
|
|
if (bind(rep->fd, (struct sockaddr *)&req.src, |
465 |
|
|
req.src.ss_len) == -1) |
466 |
|
|
lerr(1, "privproc bind"); |
467 |
|
|
} else { |
468 |
|
|
if (bind(rep->fd, (struct sockaddr*)&saddr->addr, |
469 |
|
|
saddr->addrlen) == -1) |
470 |
|
|
lerr(1, "privproc bind"); |
471 |
|
|
} |
472 |
|
|
|
473 |
|
|
if (TAILQ_EMPTY(&p->replies)) |
474 |
|
|
add = 1; |
475 |
|
|
|
476 |
|
|
TAILQ_INSERT_TAIL(&p->replies, rep, entry); |
477 |
|
|
} |
478 |
|
|
|
479 |
|
|
if (add) |
480 |
|
|
event_add(&p->push_ev, NULL); |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
void |
484 |
|
|
privproc_push(int fd, short events, void *arg) |
485 |
|
|
{ |
486 |
|
|
struct privproc *p = arg; |
487 |
|
|
struct fd_reply *rep; |
488 |
|
|
|
489 |
|
|
struct msghdr msg; |
490 |
|
|
union { |
491 |
|
|
struct cmsghdr hdr; |
492 |
|
|
char buf[CMSG_SPACE(sizeof(int))]; |
493 |
|
|
} cmsgbuf; |
494 |
|
|
struct cmsghdr *cmsg; |
495 |
|
|
struct iovec iov; |
496 |
|
|
int result = 0; |
497 |
|
|
|
498 |
|
|
while ((rep = TAILQ_FIRST(&p->replies)) != NULL) { |
499 |
|
|
memset(&msg, 0, sizeof(msg)); |
500 |
|
|
|
501 |
|
|
msg.msg_control = (caddr_t)&cmsgbuf.buf; |
502 |
|
|
msg.msg_controllen = sizeof(cmsgbuf.buf); |
503 |
|
|
cmsg = CMSG_FIRSTHDR(&msg); |
504 |
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
505 |
|
|
cmsg->cmsg_level = SOL_SOCKET; |
506 |
|
|
cmsg->cmsg_type = SCM_RIGHTS; |
507 |
|
|
*(int *)CMSG_DATA(cmsg) = rep->fd; |
508 |
|
|
|
509 |
|
|
iov.iov_base = &result; |
510 |
|
|
iov.iov_len = sizeof(int); |
511 |
|
|
msg.msg_iov = &iov; |
512 |
|
|
msg.msg_iovlen = 1; |
513 |
|
|
|
514 |
|
|
switch (sendmsg(fd, &msg, 0)) { |
515 |
|
|
case sizeof(int): |
516 |
|
|
break; |
517 |
|
|
|
518 |
|
|
case -1: |
519 |
|
|
if (errno == EAGAIN) |
520 |
|
|
goto again; |
521 |
|
|
|
522 |
|
|
lerr(1, "privproc sendmsg"); |
523 |
|
|
/* NOTREACHED */ |
524 |
|
|
|
525 |
|
|
default: |
526 |
|
|
lerrx(1, "privproc sendmsg weird len"); |
527 |
|
|
} |
528 |
|
|
|
529 |
|
|
TAILQ_REMOVE(&p->replies, rep, entry); |
530 |
|
|
close(rep->fd); |
531 |
|
|
free(rep); |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
if (TAILQ_EMPTY(&p->replies)) |
535 |
|
|
return; |
536 |
|
|
|
537 |
|
|
again: |
538 |
|
|
event_add(&p->push_ev, NULL); |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
void |
542 |
|
|
proxy_listen(const char *addr, const char *port, int family) |
543 |
|
|
{ |
544 |
|
|
struct proxy_listener *l; |
545 |
|
|
|
546 |
|
|
struct addrinfo hints, *res, *res0; |
547 |
|
|
int error; |
548 |
|
|
int s, on = 1; |
549 |
|
|
int serrno; |
550 |
|
|
const char *cause = NULL; |
551 |
|
|
|
552 |
|
|
memset(&hints, 0, sizeof(hints)); |
553 |
|
|
hints.ai_family = family; |
554 |
|
|
hints.ai_socktype = SOCK_DGRAM; |
555 |
|
|
hints.ai_flags = AI_PASSIVE; |
556 |
|
|
|
557 |
|
|
TAILQ_INIT(&proxy_listeners); |
558 |
|
|
|
559 |
|
|
error = getaddrinfo(addr, port, &hints, &res0); |
560 |
|
|
if (error) |
561 |
|
|
errx(1, "%s:%s: %s", addr, port, gai_strerror(error)); |
562 |
|
|
|
563 |
|
|
for (res = res0; res != NULL; res = res->ai_next) { |
564 |
|
|
s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK, |
565 |
|
|
res->ai_protocol); |
566 |
|
|
if (s == -1) { |
567 |
|
|
cause = "socket"; |
568 |
|
|
continue; |
569 |
|
|
} |
570 |
|
|
|
571 |
|
|
if (bind(s, res->ai_addr, res->ai_addrlen) == -1) { |
572 |
|
|
cause = "bind"; |
573 |
|
|
serrno = errno; |
574 |
|
|
close(s); |
575 |
|
|
errno = serrno; |
576 |
|
|
continue; |
577 |
|
|
} |
578 |
|
|
|
579 |
|
|
l = calloc(1, sizeof(*l)); |
580 |
|
|
if (l == NULL) |
581 |
|
|
err(1, "listener alloc"); |
582 |
|
|
|
583 |
|
|
switch (res->ai_family) { |
584 |
|
|
case AF_INET: |
585 |
|
|
l->cmsg2dst = proxy_dst4; |
586 |
|
|
|
587 |
|
|
if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, |
588 |
|
|
&on, sizeof(on)) == -1) |
589 |
|
|
errx(1, "setsockopt(IP_RECVDSTADDR)"); |
590 |
|
|
if (setsockopt(s, IPPROTO_IP, IP_RECVDSTPORT, |
591 |
|
|
&on, sizeof(on)) == -1) |
592 |
|
|
errx(1, "setsockopt(IP_RECVDSTPORT)"); |
593 |
|
|
break; |
594 |
|
|
case AF_INET6: |
595 |
|
|
l->cmsg2dst = proxy_dst6; |
596 |
|
|
|
597 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, |
598 |
|
|
&on, sizeof(on)) == -1) |
599 |
|
|
errx(1, "setsockopt(IPV6_RECVPKTINFO)"); |
600 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTPORT, |
601 |
|
|
&on, sizeof(on)) == -1) |
602 |
|
|
errx(1, "setsockopt(IPV6_RECVDSTPORT)"); |
603 |
|
|
break; |
604 |
|
|
} |
605 |
|
|
l->s = s; |
606 |
|
|
|
607 |
|
|
TAILQ_INSERT_TAIL(&proxy_listeners, l, entry); |
608 |
|
|
} |
609 |
|
|
freeaddrinfo(res0); |
610 |
|
|
|
611 |
|
|
if (TAILQ_EMPTY(&proxy_listeners)) |
612 |
|
|
err(1, "%s", cause); |
613 |
|
|
} |
614 |
|
|
|
615 |
|
|
void |
616 |
|
|
proxy_listener_events(void) |
617 |
|
|
{ |
618 |
|
|
struct proxy_listener *l; |
619 |
|
|
|
620 |
|
|
TAILQ_FOREACH(l, &proxy_listeners, entry) { |
621 |
|
|
event_set(&l->ev, l->s, EV_READ | EV_PERSIST, proxy_recv, l); |
622 |
|
|
event_add(&l->ev, NULL); |
623 |
|
|
} |
624 |
|
|
} |
625 |
|
|
|
626 |
|
|
char safety[SEGSIZE_MAX + 4]; |
627 |
|
|
|
628 |
|
|
int |
629 |
|
|
proxy_dst4(struct cmsghdr *cmsg, struct sockaddr_storage *ss) |
630 |
|
|
{ |
631 |
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)ss; |
632 |
|
|
|
633 |
|
|
if (cmsg->cmsg_level != IPPROTO_IP) |
634 |
|
|
return (0); |
635 |
|
|
|
636 |
|
|
switch (cmsg->cmsg_type) { |
637 |
|
|
case IP_RECVDSTADDR: |
638 |
|
|
memcpy(&sin->sin_addr, CMSG_DATA(cmsg), sizeof(sin->sin_addr)); |
639 |
|
|
if (sin->sin_addr.s_addr == INADDR_BROADCAST) |
640 |
|
|
return (-1); |
641 |
|
|
break; |
642 |
|
|
|
643 |
|
|
case IP_RECVDSTPORT: |
644 |
|
|
memcpy(&sin->sin_port, CMSG_DATA(cmsg), sizeof(sin->sin_port)); |
645 |
|
|
break; |
646 |
|
|
} |
647 |
|
|
|
648 |
|
|
return (0); |
649 |
|
|
} |
650 |
|
|
|
651 |
|
|
int |
652 |
|
|
proxy_dst6(struct cmsghdr *cmsg, struct sockaddr_storage *ss) |
653 |
|
|
{ |
654 |
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss; |
655 |
|
|
struct in6_pktinfo *ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg); |
656 |
|
|
|
657 |
|
|
if (cmsg->cmsg_level != IPPROTO_IPV6) |
658 |
|
|
return (0); |
659 |
|
|
|
660 |
|
|
switch (cmsg->cmsg_type) { |
661 |
|
|
case IPV6_PKTINFO: |
662 |
|
|
memcpy(&sin6->sin6_addr, &ipi->ipi6_addr, |
663 |
|
|
sizeof(sin6->sin6_addr)); |
664 |
|
|
#ifdef __KAME__ |
665 |
|
|
if (IN6_IS_ADDR_LINKLOCAL(&ipi->ipi6_addr)) |
666 |
|
|
sin6->sin6_scope_id = ipi->ipi6_ifindex; |
667 |
|
|
#endif |
668 |
|
|
break; |
669 |
|
|
case IPV6_RECVDSTPORT: |
670 |
|
|
memcpy(&sin6->sin6_port, CMSG_DATA(cmsg), |
671 |
|
|
sizeof(sin6->sin6_port)); |
672 |
|
|
break; |
673 |
|
|
} |
674 |
|
|
|
675 |
|
|
return (0); |
676 |
|
|
} |
677 |
|
|
|
678 |
|
|
void |
679 |
|
|
proxy_recv(int fd, short events, void *arg) |
680 |
|
|
{ |
681 |
|
|
struct proxy_listener *l = arg; |
682 |
|
|
|
683 |
|
|
union { |
684 |
|
|
struct cmsghdr hdr; |
685 |
|
|
char buf[CMSG_SPACE(sizeof(struct sockaddr_storage)) + |
686 |
|
|
CMSG_SPACE(sizeof(in_port_t))]; |
687 |
|
|
} cmsgbuf; |
688 |
|
|
struct cmsghdr *cmsg; |
689 |
|
|
struct msghdr msg; |
690 |
|
|
struct iovec iov; |
691 |
|
|
ssize_t n; |
692 |
|
|
|
693 |
|
|
struct proxy_request *r; |
694 |
|
|
struct tftphdr *tp; |
695 |
|
|
|
696 |
|
|
r = calloc(1, sizeof(*r)); |
697 |
|
|
if (r == NULL) { |
698 |
|
|
recv(fd, safety, sizeof(safety), 0); |
699 |
|
|
return; |
700 |
|
|
} |
701 |
|
|
r->id = arc4random(); /* XXX unique? */ |
702 |
|
|
|
703 |
|
|
bzero(&msg, sizeof(msg)); |
704 |
|
|
iov.iov_base = r->buf; |
705 |
|
|
iov.iov_len = sizeof(r->buf); |
706 |
|
|
msg.msg_name = &r->addrs.src; |
707 |
|
|
msg.msg_namelen = sizeof(r->addrs.src); |
708 |
|
|
msg.msg_iov = &iov; |
709 |
|
|
msg.msg_iovlen = 1; |
710 |
|
|
msg.msg_control = &cmsgbuf.buf; |
711 |
|
|
msg.msg_controllen = sizeof(cmsgbuf.buf); |
712 |
|
|
|
713 |
|
|
n = recvmsg(fd, &msg, 0); |
714 |
|
|
if (n == -1) { |
715 |
|
|
switch (errno) { |
716 |
|
|
case EAGAIN: |
717 |
|
|
case EINTR: |
718 |
|
|
goto err; |
719 |
|
|
default: |
720 |
|
|
lerr(1, "recvmsg"); |
721 |
|
|
/* NOTREACHED */ |
722 |
|
|
} |
723 |
|
|
} |
724 |
|
|
r->buflen = n; |
725 |
|
|
|
726 |
|
|
/* check the packet */ |
727 |
|
|
if (n < 5) { |
728 |
|
|
/* not enough to be a real packet */ |
729 |
|
|
goto err; |
730 |
|
|
} |
731 |
|
|
tp = (struct tftphdr *)r->buf; |
732 |
|
|
switch (ntohs(tp->th_opcode)) { |
733 |
|
|
case RRQ: |
734 |
|
|
case WRQ: |
735 |
|
|
break; |
736 |
|
|
default: |
737 |
|
|
goto err; |
738 |
|
|
} |
739 |
|
|
|
740 |
|
|
r->addrs.dst.ss_family = r->addrs.src.ss_family; |
741 |
|
|
r->addrs.dst.ss_len = r->addrs.src.ss_len; |
742 |
|
|
|
743 |
|
|
/* get local address if possible */ |
744 |
|
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; |
745 |
|
|
cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
746 |
|
|
if (l->cmsg2dst(cmsg, &r->addrs.dst) == -1) |
747 |
|
|
goto err; |
748 |
|
|
} |
749 |
|
|
|
750 |
|
|
if (verbose) { |
751 |
|
|
linfo("%s:%d -> %s:%d \"%s %s\"", |
752 |
|
|
sock_ntop((struct sockaddr *)&r->addrs.src), |
753 |
|
|
ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port), |
754 |
|
|
sock_ntop((struct sockaddr *)&r->addrs.dst), |
755 |
|
|
ntohs(((struct sockaddr_in *)&r->addrs.dst)->sin_port), |
756 |
|
|
opcode(ntohs(tp->th_opcode)), tp->th_stuff); |
757 |
|
|
/* XXX tp->th_stuff could be garbage */ |
758 |
|
|
} |
759 |
|
|
|
760 |
|
|
TAILQ_INSERT_TAIL(&child->fdrequests, r, entry); |
761 |
|
|
evbuffer_add(child->buf, &r->addrs, sizeof(r->addrs)); |
762 |
|
|
event_add(&child->push_ev, NULL); |
763 |
|
|
|
764 |
|
|
return; |
765 |
|
|
|
766 |
|
|
err: |
767 |
|
|
free(r); |
768 |
|
|
} |
769 |
|
|
|
770 |
|
|
void |
771 |
|
|
unprivproc_push(int fd, short events, void *arg) |
772 |
|
|
{ |
773 |
|
|
if (evbuffer_write(child->buf, fd) == -1) |
774 |
|
|
lerr(1, "child evbuffer_write"); |
775 |
|
|
|
776 |
|
|
if (EVBUFFER_LENGTH(child->buf)) |
777 |
|
|
event_add(&child->push_ev, NULL); |
778 |
|
|
} |
779 |
|
|
|
780 |
|
|
void |
781 |
|
|
unprivproc_pop(int fd, short events, void *arg) |
782 |
|
|
{ |
783 |
|
|
struct proxy_request *r; |
784 |
|
|
|
785 |
|
|
struct msghdr msg; |
786 |
|
|
union { |
787 |
|
|
struct cmsghdr hdr; |
788 |
|
|
char buf[CMSG_SPACE(sizeof(int))]; |
789 |
|
|
} cmsgbuf; |
790 |
|
|
struct cmsghdr *cmsg; |
791 |
|
|
struct iovec iov; |
792 |
|
|
struct src_addr *src_addr; |
793 |
|
|
struct sockaddr_storage saddr; |
794 |
|
|
socklen_t len; |
795 |
|
|
int result; |
796 |
|
|
int s; |
797 |
|
|
|
798 |
|
|
len = sizeof(saddr); |
799 |
|
|
|
800 |
|
|
do { |
801 |
|
|
memset(&msg, 0, sizeof(msg)); |
802 |
|
|
iov.iov_base = &result; |
803 |
|
|
iov.iov_len = sizeof(int); |
804 |
|
|
msg.msg_iov = &iov; |
805 |
|
|
msg.msg_iovlen = 1; |
806 |
|
|
msg.msg_control = &cmsgbuf.buf; |
807 |
|
|
msg.msg_controllen = sizeof(cmsgbuf.buf); |
808 |
|
|
|
809 |
|
|
switch (recvmsg(fd, &msg, 0)) { |
810 |
|
|
case sizeof(int): |
811 |
|
|
break; |
812 |
|
|
|
813 |
|
|
case -1: |
814 |
|
|
switch (errno) { |
815 |
|
|
case EAGAIN: |
816 |
|
|
case EINTR: |
817 |
|
|
return; |
818 |
|
|
default: |
819 |
|
|
lerr(1, "child recvmsg"); |
820 |
|
|
} |
821 |
|
|
/* NOTREACHED */ |
822 |
|
|
|
823 |
|
|
case 0: |
824 |
|
|
lerrx(1, "privproc closed connection"); |
825 |
|
|
|
826 |
|
|
default: |
827 |
|
|
lerrx(1, "child recvmsg was weird"); |
828 |
|
|
/* NOTREACHED */ |
829 |
|
|
} |
830 |
|
|
|
831 |
|
|
if (result != 0) { |
832 |
|
|
errno = result; |
833 |
|
|
lerr(1, "child fdpass fail"); |
834 |
|
|
} |
835 |
|
|
|
836 |
|
|
cmsg = CMSG_FIRSTHDR(&msg); |
837 |
|
|
if (cmsg == NULL) |
838 |
|
|
lerrx(1, "%s: no message header", __func__); |
839 |
|
|
|
840 |
|
|
if (cmsg->cmsg_type != SCM_RIGHTS) { |
841 |
|
|
lerrx(1, "%s: expected type %d got %d", __func__, |
842 |
|
|
SCM_RIGHTS, cmsg->cmsg_type); |
843 |
|
|
} |
844 |
|
|
|
845 |
|
|
s = (*(int *)CMSG_DATA(cmsg)); |
846 |
|
|
|
847 |
|
|
r = TAILQ_FIRST(&child->fdrequests); |
848 |
|
|
if (r == NULL) |
849 |
|
|
lerrx(1, "got fd without a pending request"); |
850 |
|
|
|
851 |
|
|
TAILQ_REMOVE(&child->fdrequests, r, entry); |
852 |
|
|
|
853 |
|
|
/* get ready to add rules */ |
854 |
|
|
if (prepare_commit(r->id) == -1) |
855 |
|
|
lerr(1, "%s: prepare_commit", __func__); |
856 |
|
|
|
857 |
|
|
TAILQ_FOREACH(src_addr, &src_addrs, entry) |
858 |
|
|
if (src_addr->addr.ss_family == r->addrs.dst.ss_family) |
859 |
|
|
break; |
860 |
|
|
if (src_addr == NULL) { |
861 |
|
|
if (add_filter(r->id, PF_IN, (struct sockaddr *) |
862 |
|
|
&r->addrs.dst, (struct sockaddr *)&r->addrs.src, |
863 |
|
|
ntohs(((struct sockaddr_in *)&r->addrs.src) |
864 |
|
|
->sin_port), IPPROTO_UDP) == -1) |
865 |
|
|
lerr(1, "%s: couldn't add pass in", __func__); |
866 |
|
|
} else { |
867 |
|
|
if (getsockname(s, (struct sockaddr*)&saddr, &len) == -1) |
868 |
|
|
lerr(1, "%s: getsockname", __func__); |
869 |
|
|
if (add_rdr(r->id, (struct sockaddr *)&r->addrs.dst, |
870 |
|
|
(struct sockaddr*)&saddr, |
871 |
|
|
ntohs(((struct sockaddr_in *)&saddr)->sin_port), |
872 |
|
|
(struct sockaddr *)&r->addrs.src, |
873 |
|
|
ntohs(((struct sockaddr_in *)&r->addrs.src)-> |
874 |
|
|
sin_port), IPPROTO_UDP ) == -1) |
875 |
|
|
lerr(1, "%s: couldn't add rdr rule", __func__); |
876 |
|
|
} |
877 |
|
|
|
878 |
|
|
if (add_filter(r->id, PF_OUT, (struct sockaddr *)&r->addrs.dst, |
879 |
|
|
(struct sockaddr *)&r->addrs.src, |
880 |
|
|
ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port), |
881 |
|
|
IPPROTO_UDP) == -1) |
882 |
|
|
lerr(1, "%s: couldn't add pass out", __func__); |
883 |
|
|
|
884 |
|
|
if (do_commit() == -1) |
885 |
|
|
lerr(1, "%s: couldn't commit rules", __func__); |
886 |
|
|
|
887 |
|
|
/* forward the initial tftp request and start the insanity */ |
888 |
|
|
if (sendto(s, r->buf, r->buflen, 0, |
889 |
|
|
(struct sockaddr *)&r->addrs.dst, |
890 |
|
|
r->addrs.dst.ss_len) == -1) |
891 |
|
|
lerr(1, "%s: unable to send", __func__); |
892 |
|
|
|
893 |
|
|
close(s); |
894 |
|
|
|
895 |
|
|
evtimer_set(&r->ev, unprivproc_timeout, r); |
896 |
|
|
evtimer_add(&r->ev, &transwait); |
897 |
|
|
|
898 |
|
|
TAILQ_INSERT_TAIL(&child->tmrequests, r, entry); |
899 |
|
|
} while (!TAILQ_EMPTY(&child->fdrequests)); |
900 |
|
|
} |
901 |
|
|
|
902 |
|
|
void |
903 |
|
|
unprivproc_timeout(int fd, short events, void *arg) |
904 |
|
|
{ |
905 |
|
|
struct proxy_request *r = arg; |
906 |
|
|
|
907 |
|
|
TAILQ_REMOVE(&child->tmrequests, r, entry); |
908 |
|
|
|
909 |
|
|
/* delete our rdr rule and clean up */ |
910 |
|
|
prepare_commit(r->id); |
911 |
|
|
do_commit(); |
912 |
|
|
|
913 |
|
|
free(r); |
914 |
|
|
} |
915 |
|
|
|
916 |
|
|
|
917 |
|
|
const char * |
918 |
|
|
opcode(int code) |
919 |
|
|
{ |
920 |
|
|
static char str[6]; |
921 |
|
|
|
922 |
|
|
switch (code) { |
923 |
|
|
case 1: |
924 |
|
|
(void)snprintf(str, sizeof(str), "RRQ"); |
925 |
|
|
break; |
926 |
|
|
case 2: |
927 |
|
|
(void)snprintf(str, sizeof(str), "WRQ"); |
928 |
|
|
break; |
929 |
|
|
default: |
930 |
|
|
(void)snprintf(str, sizeof(str), "(%d)", code); |
931 |
|
|
break; |
932 |
|
|
} |
933 |
|
|
|
934 |
|
|
return (str); |
935 |
|
|
} |
936 |
|
|
|
937 |
|
|
const char * |
938 |
|
|
sock_ntop(struct sockaddr *sa) |
939 |
|
|
{ |
940 |
|
|
static int n = 0; |
941 |
|
|
|
942 |
|
|
/* Cycle to next buffer. */ |
943 |
|
|
n = (n + 1) % NTOP_BUFS; |
944 |
|
|
ntop_buf[n][0] = '\0'; |
945 |
|
|
|
946 |
|
|
if (sa->sa_family == AF_INET) { |
947 |
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)sa; |
948 |
|
|
|
949 |
|
|
return (inet_ntop(AF_INET, &sin->sin_addr, ntop_buf[n], |
950 |
|
|
sizeof ntop_buf[0])); |
951 |
|
|
} |
952 |
|
|
|
953 |
|
|
if (sa->sa_family == AF_INET6) { |
954 |
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; |
955 |
|
|
|
956 |
|
|
return (inet_ntop(AF_INET6, &sin6->sin6_addr, ntop_buf[n], |
957 |
|
|
sizeof ntop_buf[0])); |
958 |
|
|
} |
959 |
|
|
|
960 |
|
|
return (NULL); |
961 |
|
|
} |
962 |
|
|
|
963 |
|
|
void |
964 |
|
|
syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) |
965 |
|
|
{ |
966 |
|
|
char *s; |
967 |
|
|
|
968 |
|
|
if (vasprintf(&s, fmt, ap) == -1) { |
969 |
|
|
syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); |
970 |
|
|
exit(1); |
971 |
|
|
} |
972 |
|
|
|
973 |
|
|
syslog(priority, "%s: %s", s, strerror(e)); |
974 |
|
|
|
975 |
|
|
free(s); |
976 |
|
|
} |
977 |
|
|
|
978 |
|
|
void |
979 |
|
|
syslog_err(int ecode, const char *fmt, ...) |
980 |
|
|
{ |
981 |
|
|
va_list ap; |
982 |
|
|
|
983 |
|
|
va_start(ap, fmt); |
984 |
|
|
syslog_vstrerror(errno, LOG_CRIT, fmt, ap); |
985 |
|
|
va_end(ap); |
986 |
|
|
|
987 |
|
|
exit(ecode); |
988 |
|
|
} |
989 |
|
|
|
990 |
|
|
void |
991 |
|
|
syslog_errx(int ecode, const char *fmt, ...) |
992 |
|
|
{ |
993 |
|
|
va_list ap; |
994 |
|
|
|
995 |
|
|
va_start(ap, fmt); |
996 |
|
|
vsyslog(LOG_CRIT, fmt, ap); |
997 |
|
|
va_end(ap); |
998 |
|
|
|
999 |
|
|
exit(ecode); |
1000 |
|
|
} |
1001 |
|
|
|
1002 |
|
|
void |
1003 |
|
|
syslog_warn(const char *fmt, ...) |
1004 |
|
|
{ |
1005 |
|
|
va_list ap; |
1006 |
|
|
|
1007 |
|
|
va_start(ap, fmt); |
1008 |
|
|
syslog_vstrerror(errno, LOG_ERR, fmt, ap); |
1009 |
|
|
va_end(ap); |
1010 |
|
|
} |
1011 |
|
|
|
1012 |
|
|
void |
1013 |
|
|
syslog_warnx(const char *fmt, ...) |
1014 |
|
|
{ |
1015 |
|
|
va_list ap; |
1016 |
|
|
|
1017 |
|
|
va_start(ap, fmt); |
1018 |
|
|
vsyslog(LOG_ERR, fmt, ap); |
1019 |
|
|
va_end(ap); |
1020 |
|
|
} |
1021 |
|
|
|
1022 |
|
|
void |
1023 |
|
|
syslog_info(const char *fmt, ...) |
1024 |
|
|
{ |
1025 |
|
|
va_list ap; |
1026 |
|
|
|
1027 |
|
|
va_start(ap, fmt); |
1028 |
|
|
vsyslog(LOG_INFO, fmt, ap); |
1029 |
|
|
va_end(ap); |
1030 |
|
|
} |
1031 |
|
|
|
1032 |
|
|
void |
1033 |
|
|
syslog_debug(const char *fmt, ...) |
1034 |
|
|
{ |
1035 |
|
|
va_list ap; |
1036 |
|
|
|
1037 |
|
|
if (!debug) |
1038 |
|
|
return; |
1039 |
|
|
|
1040 |
|
|
va_start(ap, fmt); |
1041 |
|
|
vsyslog(LOG_DEBUG, fmt, ap); |
1042 |
|
|
va_end(ap); |
1043 |
|
|
} |