1 /* $OpenBSD: relay_udp.c,v 1.42 2015/12/07 04:03:27 mmcc Exp $ */ 2 3 /* 4 * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/time.h> 22 #include <sys/socket.h> 23 #include <sys/tree.h> 24 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 28 #include <signal.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <stdio.h> 35 #include <event.h> 36 #include <imsg.h> 37 38 #include "relayd.h" 39 40 extern volatile sig_atomic_t relay_sessions; 41 extern objid_t relay_conid; 42 extern int proc_id; 43 extern int debug; 44 45 static struct relayd *env = NULL; 46 struct shuffle relay_shuffle; 47 48 int relay_udp_socket(struct sockaddr_storage *, in_port_t, 49 struct protocol *); 50 void relay_udp_request(struct rsession *); 51 void relay_udp_timeout(int, short, void *); 52 53 void relay_dns_log(struct rsession *, u_int8_t *, size_t); 54 void *relay_dns_validate(struct rsession *, 55 struct relay *, struct sockaddr_storage *, 56 u_int8_t *, size_t); 57 int relay_dns_request(struct rsession *); 58 void relay_udp_response(int, short, void *); 59 void relay_dns_result(struct rsession *, u_int8_t *, size_t); 60 int relay_dns_cmp(struct rsession *, struct rsession *); 61 62 void 63 relay_udp_privinit(struct relayd *x_env, struct relay *rlay) 64 { 65 if (env == NULL) 66 env = x_env; 67 68 if (rlay->rl_conf.flags & F_TLS) 69 fatalx("tls over udp is not supported"); 70 rlay->rl_conf.flags |= F_UDP; 71 } 72 73 void 74 relay_udp_init(struct relay *rlay) 75 { 76 struct protocol *proto = rlay->rl_proto; 77 78 switch (proto->type) { 79 case RELAY_PROTO_DNS: 80 proto->validate = relay_dns_validate; 81 proto->request = relay_dns_request; 82 proto->cmp = relay_dns_cmp; 83 shuffle_init(&relay_shuffle); 84 break; 85 default: 86 fatalx("unsupported udp protocol"); 87 break; 88 } 89 } 90 91 int 92 relay_udp_bind(struct sockaddr_storage *ss, in_port_t port, 93 struct protocol *proto) 94 { 95 int s; 96 97 if ((s = relay_udp_socket(ss, port, proto)) == -1) 98 return (-1); 99 100 if (bind(s, (struct sockaddr *)ss, ss->ss_len) == -1) 101 goto bad; 102 103 return (s); 104 105 bad: 106 close(s); 107 return (-1); 108 } 109 110 int 111 relay_udp_socket(struct sockaddr_storage *ss, in_port_t port, 112 struct protocol *proto) 113 { 114 int s = -1, val; 115 116 if (relay_socket_af(ss, port) == -1) 117 goto bad; 118 119 if ((s = socket(ss->ss_family, SOCK_DGRAM | SOCK_NONBLOCK, 120 IPPROTO_UDP)) == -1) 121 goto bad; 122 123 /* 124 * Socket options 125 */ 126 if (proto->tcpflags & TCPFLAG_BUFSIZ) { 127 val = proto->tcpbufsiz; 128 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, 129 &val, sizeof(val)) == -1) 130 goto bad; 131 val = proto->tcpbufsiz; 132 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, 133 &val, sizeof(val)) == -1) 134 goto bad; 135 } 136 137 /* 138 * IP options 139 */ 140 if (proto->tcpflags & TCPFLAG_IPTTL) { 141 val = (int)proto->tcpipttl; 142 if (setsockopt(s, IPPROTO_IP, IP_TTL, 143 &val, sizeof(val)) == -1) 144 goto bad; 145 } 146 if (proto->tcpflags & TCPFLAG_IPMINTTL) { 147 val = (int)proto->tcpipminttl; 148 if (setsockopt(s, IPPROTO_IP, IP_MINTTL, 149 &val, sizeof(val)) == -1) 150 goto bad; 151 } 152 153 return (s); 154 155 bad: 156 if (s != -1) 157 close(s); 158 return (-1); 159 } 160 161 void 162 relay_udp_response(int fd, short sig, void *arg) 163 { 164 struct rsession *con = arg; 165 struct relay *rlay = con->se_relay; 166 struct protocol *proto = rlay->rl_proto; 167 void *priv = NULL; 168 struct sockaddr_storage ss; 169 u_int8_t buf[IBUF_READ_SIZE]; 170 ssize_t len; 171 socklen_t slen; 172 173 if (sig == EV_TIMEOUT) { 174 relay_udp_timeout(fd, sig, arg); 175 return; 176 } 177 178 if (relay_sessions >= RELAY_MAX_SESSIONS || 179 rlay->rl_conf.flags & F_DISABLE) 180 return; 181 182 slen = sizeof(ss); 183 if ((len = recvfrom(fd, buf, sizeof(buf), 0, 184 (struct sockaddr*)&ss, &slen)) < 1) 185 return; 186 187 /* Parse and validate the packet header */ 188 if (proto->validate != NULL && 189 (priv = (*proto->validate)(con, rlay, &ss, buf, len)) == NULL) 190 return; 191 192 relay_close(con, "unknown response"); 193 free(priv); 194 } 195 196 void 197 relay_udp_server(int fd, short sig, void *arg) 198 { 199 struct relay *rlay = arg; 200 struct protocol *proto = rlay->rl_proto; 201 struct rsession *con = NULL; 202 struct ctl_natlook *cnl = NULL; 203 socklen_t slen; 204 struct timeval tv; 205 struct sockaddr_storage ss; 206 u_int8_t buf[IBUF_READ_SIZE]; 207 void *priv = NULL; 208 ssize_t len; 209 210 event_add(&rlay->rl_ev, NULL); 211 212 if (relay_sessions >= RELAY_MAX_SESSIONS || 213 rlay->rl_conf.flags & F_DISABLE) 214 return; 215 216 slen = sizeof(ss); 217 if ((len = recvfrom(fd, buf, sizeof(buf), 0, 218 (struct sockaddr*)&ss, &slen)) < 1) 219 return; 220 221 if (proto->validate != NULL && 222 (priv = (*proto->validate)(NULL, rlay, &ss, buf, len)) == NULL) 223 return; 224 225 if ((con = calloc(1, sizeof(*con))) == NULL) { 226 free(priv); 227 return; 228 } 229 230 /* 231 * Replace the DNS request Id with a random Id. 232 */ 233 con->se_priv = priv; 234 con->se_in.s = -1; 235 con->se_out.s = -1; 236 con->se_in.dst = &con->se_out; 237 con->se_out.dst = &con->se_in; 238 con->se_in.con = con; 239 con->se_out.con = con; 240 con->se_relay = rlay; 241 con->se_id = ++relay_conid; 242 con->se_in.dir = RELAY_DIR_REQUEST; 243 con->se_out.dir = RELAY_DIR_RESPONSE; 244 con->se_retry = rlay->rl_conf.dstretry; 245 con->se_out.port = rlay->rl_conf.dstport; 246 switch (ss.ss_family) { 247 case AF_INET: 248 con->se_in.port = ((struct sockaddr_in *)&ss)->sin_port; 249 break; 250 case AF_INET6: 251 con->se_in.port = ((struct sockaddr_in6 *)&ss)->sin6_port; 252 break; 253 } 254 bcopy(&ss, &con->se_in.ss, sizeof(con->se_in.ss)); 255 256 getmonotime(&con->se_tv_start); 257 bcopy(&con->se_tv_start, &con->se_tv_last, sizeof(con->se_tv_last)); 258 259 relay_sessions++; 260 SPLAY_INSERT(session_tree, &rlay->rl_sessions, con); 261 relay_session_publish(con); 262 263 /* Increment the per-relay session counter */ 264 rlay->rl_stats[proc_id].last++; 265 266 /* Pre-allocate output buffer */ 267 con->se_out.output = evbuffer_new(); 268 if (con->se_out.output == NULL) { 269 relay_close(con, "failed to allocate output buffer"); 270 return; 271 } 272 273 /* Pre-allocate log buffer */ 274 con->se_haslog = 0; 275 con->se_log = evbuffer_new(); 276 if (con->se_log == NULL) { 277 relay_close(con, "failed to allocate log buffer"); 278 return; 279 } 280 281 if (rlay->rl_conf.flags & F_NATLOOK) { 282 if ((cnl = calloc(1, sizeof(*cnl))) == NULL) { 283 relay_close(con, "failed to allocate natlookup"); 284 return; 285 } 286 } 287 288 /* Save the received data */ 289 if (evbuffer_add(con->se_out.output, buf, len) == -1) { 290 relay_close(con, "failed to store buffer"); 291 free(cnl); 292 return; 293 } 294 295 if (cnl != NULL) { 296 con->se_cnl = cnl; 297 bzero(cnl, sizeof(*cnl)); 298 cnl->in = -1; 299 cnl->id = con->se_id; 300 cnl->proc = proc_id; 301 cnl->proto = IPPROTO_UDP; 302 bcopy(&con->se_in.ss, &cnl->src, sizeof(cnl->src)); 303 bcopy(&rlay->rl_conf.ss, &cnl->dst, sizeof(cnl->dst)); 304 proc_compose(env->sc_ps, PROC_PFE, 305 IMSG_NATLOOK, cnl, sizeof(*cnl)); 306 307 /* Schedule timeout */ 308 evtimer_set(&con->se_ev, relay_natlook, con); 309 bcopy(&rlay->rl_conf.timeout, &tv, sizeof(tv)); 310 evtimer_add(&con->se_ev, &tv); 311 return; 312 } 313 314 relay_session(con); 315 } 316 317 void 318 relay_udp_timeout(int fd, short sig, void *arg) 319 { 320 struct rsession *con = arg; 321 322 if (sig != EV_TIMEOUT) 323 fatalx("invalid timeout event"); 324 325 relay_close(con, "udp timeout"); 326 } 327 328 /* 329 * Domain Name System support 330 */ 331 332 struct relay_dns_priv { 333 u_int16_t dp_inkey; 334 u_int16_t dp_outkey; 335 }; 336 337 struct relay_dnshdr { 338 u_int16_t dns_id; 339 340 u_int8_t dns_flags0; 341 #define DNS_F0_QR 0x80 /* response flag */ 342 #define DNS_F0_OPCODE 0x78 /* message type */ 343 #define DNS_F0_AA 0x04 /* authorative answer */ 344 #define DNS_F0_TC 0x02 /* truncated message */ 345 #define DNS_F0_RD 0x01 /* recursion desired */ 346 347 u_int8_t dns_flags1; 348 #define DNS_F1_RA 0x80 /* recursion available */ 349 #define DNS_F1_RES 0x40 /* reserved */ 350 #define DNS_F1_AD 0x20 /* authentic data */ 351 #define DNS_F1_CD 0x10 /* checking disabled */ 352 #define DNS_F1_RCODE 0x0f /* response code */ 353 354 u_int16_t dns_qdcount; 355 u_int16_t dns_ancount; 356 u_int16_t dns_nscount; 357 u_int16_t dns_arcount; 358 } __packed; 359 360 void 361 relay_dns_log(struct rsession *con, u_int8_t *buf, size_t len) 362 { 363 struct relay_dnshdr *hdr = (struct relay_dnshdr *)buf; 364 365 /* Validate the header length */ 366 if (len < sizeof(*hdr)) { 367 log_debug("%s: session %d: short dns packet", __func__, 368 con->se_id); 369 return; 370 } 371 372 log_debug("%s: session %d: %s id 0x%x " 373 "flags 0x%x:0x%x qd %u an %u ns %u ar %u", __func__, 374 con->se_id, 375 hdr->dns_flags0 & DNS_F0_QR ? "response" : "request", 376 ntohs(hdr->dns_id), 377 hdr->dns_flags0, 378 hdr->dns_flags1, 379 ntohs(hdr->dns_qdcount), 380 ntohs(hdr->dns_ancount), 381 ntohs(hdr->dns_nscount), 382 ntohs(hdr->dns_arcount)); 383 } 384 385 void * 386 relay_dns_validate(struct rsession *con, struct relay *rlay, 387 struct sockaddr_storage *ss, u_int8_t *buf, size_t len) 388 { 389 struct relay_dnshdr *hdr = (struct relay_dnshdr *)buf; 390 struct rsession lookup; 391 u_int16_t key; 392 struct relay_dns_priv *priv, lpriv; 393 394 /* Validate the header length */ 395 if (len < sizeof(*hdr)) 396 return (NULL); 397 398 key = ntohs(hdr->dns_id); 399 400 /* 401 * Check if the header has the response flag set, otherwise 402 * return 0 to tell the UDP server to create a new session. 403 */ 404 if ((hdr->dns_flags0 & DNS_F0_QR) == 0) { 405 priv = malloc(sizeof(struct relay_dns_priv)); 406 if (priv == NULL) 407 return (NULL); 408 priv->dp_inkey = shuffle_generate16(&relay_shuffle); 409 priv->dp_outkey = key; 410 return ((void *)priv); 411 } 412 413 /* 414 * Lookup if this response is for a known session and if the 415 * remote host matches the original destination of the request. 416 */ 417 if (con == NULL) { 418 lpriv.dp_inkey = key; 419 lookup.se_priv = &lpriv; 420 if ((con = SPLAY_FIND(session_tree, 421 &rlay->rl_sessions, &lookup)) != NULL && 422 con->se_priv != NULL && 423 relay_cmp_af(ss, &con->se_out.ss) == 0) 424 relay_dns_result(con, buf, len); 425 } else { 426 priv = con->se_priv; 427 if (priv == NULL || key != priv->dp_inkey) { 428 relay_close(con, "invalid response"); 429 return (NULL); 430 } 431 relay_dns_result(con, buf, len); 432 } 433 434 /* 435 * This is not a new session, ignore it in the UDP server. 436 */ 437 return (NULL); 438 } 439 440 int 441 relay_dns_request(struct rsession *con) 442 { 443 struct relay *rlay = con->se_relay; 444 struct relay_dns_priv *priv = con->se_priv; 445 u_int8_t *buf = EVBUFFER_DATA(con->se_out.output); 446 size_t len = EVBUFFER_LENGTH(con->se_out.output); 447 struct relay_dnshdr *hdr; 448 socklen_t slen; 449 450 if (buf == NULL || priv == NULL || len < 1) 451 return (-1); 452 if (debug) 453 relay_dns_log(con, buf, len); 454 455 getmonotime(&con->se_tv_start); 456 457 if (!TAILQ_EMPTY(&rlay->rl_tables)) { 458 if (relay_from_table(con) != 0) 459 return (-1); 460 } else if (con->se_out.ss.ss_family == AF_UNSPEC) { 461 bcopy(&rlay->rl_conf.dstss, &con->se_out.ss, 462 sizeof(con->se_out.ss)); 463 con->se_out.port = rlay->rl_conf.dstport; 464 } 465 466 if ((con->se_out.s = relay_udp_socket(&con->se_out.ss, 467 con->se_out.port, rlay->rl_proto)) == -1) 468 return (-1); 469 slen = con->se_out.ss.ss_len; 470 471 hdr = (struct relay_dnshdr *)buf; 472 hdr->dns_id = htons(priv->dp_inkey); 473 474 retry: 475 if (sendto(con->se_out.s, buf, len, 0, 476 (struct sockaddr *)&con->se_out.ss, slen) == -1) { 477 if (con->se_retry) { 478 con->se_retry--; 479 log_debug("%s: session %d: " 480 "forward failed: %s, %s", __func__, 481 con->se_id, strerror(errno), 482 con->se_retry ? "next retry" : "last retry"); 483 goto retry; 484 } 485 log_debug("%s: session %d: forward failed: %s", __func__, 486 con->se_id, strerror(errno)); 487 return (-1); 488 } 489 490 event_again(&con->se_ev, con->se_out.s, EV_TIMEOUT|EV_READ, 491 relay_udp_response, &con->se_tv_start, &rlay->rl_conf.timeout, con); 492 493 return (0); 494 } 495 496 void 497 relay_dns_result(struct rsession *con, u_int8_t *buf, size_t len) 498 { 499 struct relay *rlay = con->se_relay; 500 struct relay_dns_priv *priv = con->se_priv; 501 struct relay_dnshdr *hdr; 502 socklen_t slen; 503 504 if (priv == NULL) 505 fatalx("relay_dns_result: response to invalid session"); 506 507 if (debug) 508 relay_dns_log(con, buf, len); 509 510 /* 511 * Replace the random DNS request Id with the original Id 512 */ 513 hdr = (struct relay_dnshdr *)buf; 514 hdr->dns_id = htons(priv->dp_outkey); 515 516 slen = con->se_out.ss.ss_len; 517 if (sendto(rlay->rl_s, buf, len, 0, 518 (struct sockaddr *)&con->se_in.ss, slen) == -1) { 519 relay_close(con, "response failed"); 520 return; 521 } 522 523 relay_close(con, "session closed"); 524 } 525 526 int 527 relay_dns_cmp(struct rsession *a, struct rsession *b) 528 { 529 struct relay_dns_priv *ap = a->se_priv; 530 struct relay_dns_priv *bp = b->se_priv; 531 532 if (ap == NULL || bp == NULL) 533 fatalx("relay_dns_cmp: invalid session"); 534 535 return (memcmp(&ap->dp_inkey, &bp->dp_inkey, sizeof(u_int16_t))); 536 } 537