1 /* file: alias_proxy.c 2 3 This file encapsulates special operations related to transparent 4 proxy redirection. This is where packets with a particular destination, 5 usually tcp port 80, are redirected to a proxy server. 6 7 When packets are proxied, the destination address and port are 8 modified. In certain cases, it is necessary to somehow encode 9 the original address/port info into the packet. Two methods are 10 presently supported: addition of a [DEST addr port] string at the 11 beginning a of tcp stream, or inclusion of an optional field 12 in the IP header. 13 14 There is one public API function: 15 16 PacketAliasProxyRule() -- Adds and deletes proxy 17 rules. 18 19 Rules are stored in a linear linked list, so lookup efficiency 20 won't be too good for large lists. 21 22 23 Initial development: April, 1998 (cjm) 24 25 $FreeBSD$ 26 */ 27 28 29 /* System includes */ 30 #include <ctype.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <netdb.h> 35 36 #include <sys/types.h> 37 #include <sys/socket.h> 38 39 /* BSD IPV4 includes */ 40 #include <netinet/in_systm.h> 41 #include <netinet/in.h> 42 #include <netinet/ip.h> 43 #include <netinet/tcp.h> 44 45 #include <arpa/inet.h> 46 47 #include "alias_local.h" /* Functions used by alias*.c */ 48 #include "alias.h" /* Public API functions for libalias */ 49 50 51 52 /* 53 Data structures 54 */ 55 56 /* 57 * A linked list of arbitrary length, based on struct proxy_entry is 58 * used to store proxy rules. 59 */ 60 struct proxy_entry 61 { 62 #define PROXY_TYPE_ENCODE_NONE 1 63 #define PROXY_TYPE_ENCODE_TCPSTREAM 2 64 #define PROXY_TYPE_ENCODE_IPHDR 3 65 int rule_index; 66 int proxy_type; 67 u_char proto; 68 u_short proxy_port; 69 u_short server_port; 70 71 struct in_addr server_addr; 72 73 struct in_addr src_addr; 74 struct in_addr src_mask; 75 76 struct in_addr dst_addr; 77 struct in_addr dst_mask; 78 79 struct proxy_entry *next; 80 struct proxy_entry *last; 81 }; 82 83 84 85 /* 86 File scope variables 87 */ 88 89 static struct proxy_entry *proxyList; 90 91 92 93 /* Local (static) functions: 94 95 IpMask() -- Utility function for creating IP 96 masks from integer (1-32) specification. 97 IpAddr() -- Utility function for converting string 98 to IP address 99 IpPort() -- Utility function for converting string 100 to port number 101 RuleAdd() -- Adds an element to the rule list. 102 RuleDelete() -- Removes an element from the rule list. 103 RuleNumberDelete() -- Removes all elements from the rule list 104 having a certain rule number. 105 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning 106 of a TCP stream. 107 ProxyEncodeIpHeader() -- Adds an IP option indicating the true 108 destination of a proxied IP packet 109 */ 110 111 static int IpMask(int, struct in_addr *); 112 static int IpAddr(char *, struct in_addr *); 113 static int IpPort(char *, int, int *); 114 static void RuleAdd(struct proxy_entry *); 115 static void RuleDelete(struct proxy_entry *); 116 static int RuleNumberDelete(int); 117 static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int); 118 static void ProxyEncodeIpHeader(struct ip *, int); 119 120 static int 121 IpMask(int nbits, struct in_addr *mask) 122 { 123 int i; 124 u_int imask; 125 126 if (nbits < 0 || nbits > 32) 127 return -1; 128 129 imask = 0; 130 for (i=0; i<nbits; i++) 131 imask = (imask >> 1) + 0x80000000; 132 mask->s_addr = htonl(imask); 133 134 return 0; 135 } 136 137 static int 138 IpAddr(char *s, struct in_addr *addr) 139 { 140 if (inet_aton(s, addr) == 0) 141 return -1; 142 else 143 return 0; 144 } 145 146 static int 147 IpPort(char *s, int proto, int *port) 148 { 149 int n; 150 151 n = sscanf(s, "%d", port); 152 if (n != 1) 153 { 154 struct servent *se; 155 156 if (proto == IPPROTO_TCP) 157 se = getservbyname(s, "tcp"); 158 else if (proto == IPPROTO_UDP) 159 se = getservbyname(s, "udp"); 160 else 161 return -1; 162 163 if (se == NULL) 164 return -1; 165 166 *port = (u_int) ntohs(se->s_port); 167 } 168 169 return 0; 170 } 171 172 void 173 RuleAdd(struct proxy_entry *entry) 174 { 175 int rule_index; 176 struct proxy_entry *ptr; 177 struct proxy_entry *ptr_last; 178 179 if (proxyList == NULL) 180 { 181 proxyList = entry; 182 entry->last = NULL; 183 entry->next = NULL; 184 return; 185 } 186 187 rule_index = entry->rule_index; 188 ptr = proxyList; 189 ptr_last = NULL; 190 while (ptr != NULL) 191 { 192 if (ptr->rule_index >= rule_index) 193 { 194 if (ptr_last == NULL) 195 { 196 entry->next = proxyList; 197 entry->last = NULL; 198 proxyList->last = entry; 199 proxyList = entry; 200 return; 201 } 202 203 ptr_last->next = entry; 204 ptr->last = entry; 205 entry->last = ptr->last; 206 entry->next = ptr; 207 return; 208 } 209 ptr_last = ptr; 210 ptr = ptr->next; 211 } 212 213 ptr_last->next = entry; 214 entry->last = ptr_last; 215 entry->next = NULL; 216 } 217 218 static void 219 RuleDelete(struct proxy_entry *entry) 220 { 221 if (entry->last != NULL) 222 entry->last->next = entry->next; 223 else 224 proxyList = entry->next; 225 226 if (entry->next != NULL) 227 entry->next->last = entry->last; 228 229 free(entry); 230 } 231 232 static int 233 RuleNumberDelete(int rule_index) 234 { 235 int err; 236 struct proxy_entry *ptr; 237 238 err = -1; 239 ptr = proxyList; 240 while (ptr != NULL) 241 { 242 struct proxy_entry *ptr_next; 243 244 ptr_next = ptr->next; 245 if (ptr->rule_index == rule_index) 246 { 247 err = 0; 248 RuleDelete(ptr); 249 } 250 251 ptr = ptr_next; 252 } 253 254 return err; 255 } 256 257 static void 258 ProxyEncodeTcpStream(struct alias_link *link, 259 struct ip *pip, 260 int maxpacketsize) 261 { 262 int slen; 263 char buffer[40]; 264 struct tcphdr *tc; 265 266 /* Compute pointer to tcp header */ 267 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 268 269 /* Don't modify if once already modified */ 270 271 if (GetAckModified (link)) 272 return; 273 274 /* Translate destination address and port to string form */ 275 snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]", 276 inet_ntoa(GetProxyAddress (link)), (u_int) ntohs(GetProxyPort (link))); 277 278 /* Pad string out to a multiple of two in length */ 279 slen = strlen(buffer); 280 switch (slen % 2) 281 { 282 case 0: 283 strcat(buffer, " \n"); 284 slen += 2; 285 break; 286 case 1: 287 strcat(buffer, "\n"); 288 slen += 1; 289 } 290 291 /* Check for packet overflow */ 292 if ((ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize) 293 return; 294 295 /* Shift existing TCP data and insert destination string */ 296 { 297 int dlen; 298 int hlen; 299 u_char *p; 300 301 hlen = (pip->ip_hl + tc->th_off) << 2; 302 dlen = ntohs (pip->ip_len) - hlen; 303 304 /* Modify first packet that has data in it */ 305 306 if (dlen == 0) 307 return; 308 309 p = (char *) pip; 310 p += hlen; 311 312 memmove(p + slen, p, dlen); 313 memcpy(p, buffer, slen); 314 } 315 316 /* Save information about modfied sequence number */ 317 { 318 int delta; 319 320 SetAckModified(link); 321 delta = GetDeltaSeqOut(pip, link); 322 AddSeq(pip, link, delta+slen); 323 } 324 325 /* Update IP header packet length and checksum */ 326 { 327 int accumulate; 328 329 accumulate = pip->ip_len; 330 pip->ip_len = htons(ntohs(pip->ip_len) + slen); 331 accumulate -= pip->ip_len; 332 333 ADJUST_CHECKSUM(accumulate, pip->ip_sum); 334 } 335 336 /* Update TCP checksum, Use TcpChecksum since so many things have 337 already changed. */ 338 339 tc->th_sum = 0; 340 tc->th_sum = TcpChecksum (pip); 341 } 342 343 static void 344 ProxyEncodeIpHeader(struct ip *pip, 345 int maxpacketsize) 346 { 347 #define OPTION_LEN_BYTES 8 348 #define OPTION_LEN_INT16 4 349 #define OPTION_LEN_INT32 2 350 u_char option[OPTION_LEN_BYTES]; 351 352 #ifdef DEBUG 353 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip)); 354 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip)); 355 #endif 356 357 /* Check to see that there is room to add an IP option */ 358 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32)) 359 return; 360 361 /* Build option and copy into packet */ 362 { 363 u_char *ptr; 364 struct tcphdr *tc; 365 366 ptr = (u_char *) pip; 367 ptr += 20; 368 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20); 369 370 option[0] = 0x64; /* class: 3 (reserved), option 4 */ 371 option[1] = OPTION_LEN_BYTES; 372 373 memcpy(&option[2], (u_char *) &pip->ip_dst, 4); 374 375 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 376 memcpy(&option[6], (u_char *) &tc->th_sport, 2); 377 378 memcpy(ptr, option, 8); 379 } 380 381 /* Update checksum, header length and packet length */ 382 { 383 int i; 384 int accumulate; 385 u_short *sptr; 386 387 sptr = (u_short *) option; 388 accumulate = 0; 389 for (i=0; i<OPTION_LEN_INT16; i++) 390 accumulate -= *(sptr++); 391 392 sptr = (u_short *) pip; 393 accumulate += *sptr; 394 pip->ip_hl += OPTION_LEN_INT32; 395 accumulate -= *sptr; 396 397 accumulate += pip->ip_len; 398 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES); 399 accumulate -= pip->ip_len; 400 401 ADJUST_CHECKSUM(accumulate, pip->ip_sum); 402 } 403 #undef OPTION_LEN_BYTES 404 #undef OPTION_LEN_INT16 405 #undef OPTION_LEN_INT32 406 #ifdef DEBUG 407 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip)); 408 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip)); 409 #endif 410 } 411 412 413 /* Functions by other packet alias source files 414 415 ProxyCheck() -- Checks whether an outgoing packet should 416 be proxied. 417 ProxyModify() -- Encodes the original destination address/port 418 for a packet which is to be redirected to 419 a proxy server. 420 */ 421 422 int 423 ProxyCheck(struct ip *pip, 424 struct in_addr *proxy_server_addr, 425 u_short *proxy_server_port) 426 { 427 u_short dst_port; 428 struct in_addr src_addr; 429 struct in_addr dst_addr; 430 struct proxy_entry *ptr; 431 432 src_addr = pip->ip_src; 433 dst_addr = pip->ip_dst; 434 dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2))) 435 ->th_dport; 436 437 ptr = proxyList; 438 while (ptr != NULL) 439 { 440 u_short proxy_port; 441 442 proxy_port = ptr->proxy_port; 443 if ((dst_port == proxy_port || proxy_port == 0) 444 && pip->ip_p == ptr->proto 445 && src_addr.s_addr != ptr->server_addr.s_addr) 446 { 447 struct in_addr src_addr_masked; 448 struct in_addr dst_addr_masked; 449 450 src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr; 451 dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr; 452 453 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr) 454 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) 455 { 456 if ((*proxy_server_port = ptr->server_port) == 0) 457 *proxy_server_port = dst_port; 458 *proxy_server_addr = ptr->server_addr; 459 return ptr->proxy_type; 460 } 461 } 462 ptr = ptr->next; 463 } 464 465 return 0; 466 } 467 468 void 469 ProxyModify(struct alias_link *link, 470 struct ip *pip, 471 int maxpacketsize, 472 int proxy_type) 473 { 474 switch (proxy_type) 475 { 476 case PROXY_TYPE_ENCODE_IPHDR: 477 ProxyEncodeIpHeader(pip, maxpacketsize); 478 break; 479 480 case PROXY_TYPE_ENCODE_TCPSTREAM: 481 ProxyEncodeTcpStream(link, pip, maxpacketsize); 482 break; 483 } 484 } 485 486 487 /* 488 Public API functions 489 */ 490 491 int 492 PacketAliasProxyRule(const char *cmd) 493 { 494 /* 495 * This function takes command strings of the form: 496 * 497 * server <addr>[:<port>] 498 * [port <port>] 499 * [rule n] 500 * [proto tcp|udp] 501 * [src <addr>[/n]] 502 * [dst <addr>[/n]] 503 * [type encode_tcp_stream|encode_ip_hdr|no_encode] 504 * 505 * delete <rule number> 506 * 507 * Subfields can be in arbitrary order. Port numbers and addresses 508 * must be in either numeric or symbolic form. An optional rule number 509 * is used to control the order in which rules are searched. If two 510 * rules have the same number, then search order cannot be guaranteed, 511 * and the rules should be disjoint. If no rule number is specified, 512 * then 0 is used, and group 0 rules are always checked before any 513 * others. 514 */ 515 int i, n, len; 516 int cmd_len; 517 int token_count; 518 int state; 519 char *token; 520 char buffer[256]; 521 char str_port[sizeof(buffer)]; 522 char str_server_port[sizeof(buffer)]; 523 524 int rule_index; 525 int proto; 526 int proxy_type; 527 int proxy_port; 528 int server_port; 529 struct in_addr server_addr; 530 struct in_addr src_addr, src_mask; 531 struct in_addr dst_addr, dst_mask; 532 struct proxy_entry *proxy_entry; 533 534 /* Copy command line into a buffer */ 535 cmd_len = strlen(cmd); 536 if (cmd_len > (sizeof(buffer) - 1)) 537 return -1; 538 strcpy(buffer, cmd); 539 540 /* Convert to lower case */ 541 len = strlen(buffer); 542 for (i=0; i<len; i++) 543 buffer[i] = tolower(buffer[i]); 544 545 /* Set default proxy type */ 546 547 /* Set up default values */ 548 rule_index = 0; 549 proxy_type = PROXY_TYPE_ENCODE_NONE; 550 proto = IPPROTO_TCP; 551 proxy_port = 0; 552 server_addr.s_addr = 0; 553 server_port = 0; 554 src_addr.s_addr = 0; 555 IpMask(0, &src_mask); 556 dst_addr.s_addr = 0; 557 IpMask(0, &dst_mask); 558 559 str_port[0] = 0; 560 str_server_port[0] = 0; 561 562 /* Parse command string with state machine */ 563 #define STATE_READ_KEYWORD 0 564 #define STATE_READ_TYPE 1 565 #define STATE_READ_PORT 2 566 #define STATE_READ_SERVER 3 567 #define STATE_READ_RULE 4 568 #define STATE_READ_DELETE 5 569 #define STATE_READ_PROTO 6 570 #define STATE_READ_SRC 7 571 #define STATE_READ_DST 8 572 state = STATE_READ_KEYWORD; 573 token = strtok(buffer, " \t"); 574 token_count = 0; 575 while (token != NULL) 576 { 577 token_count++; 578 switch (state) 579 { 580 case STATE_READ_KEYWORD: 581 if (strcmp(token, "type") == 0) 582 state = STATE_READ_TYPE; 583 else if (strcmp(token, "port") == 0) 584 state = STATE_READ_PORT; 585 else if (strcmp(token, "server") == 0) 586 state = STATE_READ_SERVER; 587 else if (strcmp(token, "rule") == 0) 588 state = STATE_READ_RULE; 589 else if (strcmp(token, "delete") == 0) 590 state = STATE_READ_DELETE; 591 else if (strcmp(token, "proto") == 0) 592 state = STATE_READ_PROTO; 593 else if (strcmp(token, "src") == 0) 594 state = STATE_READ_SRC; 595 else if (strcmp(token, "dst") == 0) 596 state = STATE_READ_DST; 597 else 598 return -1; 599 break; 600 601 case STATE_READ_TYPE: 602 if (strcmp(token, "encode_ip_hdr") == 0) 603 proxy_type = PROXY_TYPE_ENCODE_IPHDR; 604 else if (strcmp(token, "encode_tcp_stream") == 0) 605 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM; 606 else if (strcmp(token, "no_encode") == 0) 607 proxy_type = PROXY_TYPE_ENCODE_NONE; 608 else 609 return -1; 610 state = STATE_READ_KEYWORD; 611 break; 612 613 case STATE_READ_PORT: 614 strcpy(str_port, token); 615 state = STATE_READ_KEYWORD; 616 break; 617 618 case STATE_READ_SERVER: 619 { 620 int err; 621 char *p; 622 char s[sizeof(buffer)]; 623 624 p = token; 625 while (*p != ':' && *p != 0) 626 p++; 627 628 if (*p != ':') 629 { 630 err = IpAddr(token, &server_addr); 631 if (err) 632 return -1; 633 } 634 else 635 { 636 *p = ' '; 637 638 n = sscanf(token, "%s %s", s, str_server_port); 639 if (n != 2) 640 return -1; 641 642 err = IpAddr(s, &server_addr); 643 if (err) 644 return -1; 645 } 646 } 647 state = STATE_READ_KEYWORD; 648 break; 649 650 case STATE_READ_RULE: 651 n = sscanf(token, "%d", &rule_index); 652 if (n != 1 || rule_index < 0) 653 return -1; 654 state = STATE_READ_KEYWORD; 655 break; 656 657 case STATE_READ_DELETE: 658 { 659 int err; 660 int rule_to_delete; 661 662 if (token_count != 2) 663 return -1; 664 665 n = sscanf(token, "%d", &rule_to_delete); 666 if (n != 1) 667 return -1; 668 err = RuleNumberDelete(rule_to_delete); 669 if (err) 670 return -1; 671 return 0; 672 } 673 674 case STATE_READ_PROTO: 675 if (strcmp(token, "tcp") == 0) 676 proto = IPPROTO_TCP; 677 else if (strcmp(token, "udp") == 0) 678 proto = IPPROTO_UDP; 679 else 680 return -1; 681 state = STATE_READ_KEYWORD; 682 break; 683 684 case STATE_READ_SRC: 685 case STATE_READ_DST: 686 { 687 int err; 688 char *p; 689 struct in_addr mask; 690 struct in_addr addr; 691 692 p = token; 693 while (*p != '/' && *p != 0) 694 p++; 695 696 if (*p != '/') 697 { 698 IpMask(32, &mask); 699 err = IpAddr(token, &addr); 700 if (err) 701 return -1; 702 } 703 else 704 { 705 int n; 706 int nbits; 707 char s[sizeof(buffer)]; 708 709 *p = ' '; 710 n = sscanf(token, "%s %d", s, &nbits); 711 if (n != 2) 712 return -1; 713 714 err = IpAddr(s, &addr); 715 if (err) 716 return -1; 717 718 err = IpMask(nbits, &mask); 719 if (err) 720 return -1; 721 } 722 723 if (state == STATE_READ_SRC) 724 { 725 src_addr = addr; 726 src_mask = mask; 727 } 728 else 729 { 730 dst_addr = addr; 731 dst_mask = mask; 732 } 733 } 734 state = STATE_READ_KEYWORD; 735 break; 736 737 default: 738 return -1; 739 break; 740 } 741 742 token = strtok(NULL, " \t"); 743 } 744 #undef STATE_READ_KEYWORD 745 #undef STATE_READ_TYPE 746 #undef STATE_READ_PORT 747 #undef STATE_READ_SERVER 748 #undef STATE_READ_RULE 749 #undef STATE_READ_DELETE 750 #undef STATE_READ_PROTO 751 #undef STATE_READ_SRC 752 #undef STATE_READ_DST 753 754 /* Convert port strings to numbers. This needs to be done after 755 the string is parsed, because the prototype might not be designated 756 before the ports (which might be symbolic entries in /etc/services) */ 757 758 if (strlen(str_port) != 0) 759 { 760 int err; 761 762 err = IpPort(str_port, proto, &proxy_port); 763 if (err) 764 return -1; 765 } 766 else 767 { 768 proxy_port = 0; 769 } 770 771 if (strlen(str_server_port) != 0) 772 { 773 int err; 774 775 err = IpPort(str_server_port, proto, &server_port); 776 if (err) 777 return -1; 778 } 779 else 780 { 781 server_port = 0; 782 } 783 784 /* Check that at least the server address has been defined */ 785 if (server_addr.s_addr == 0) 786 return -1; 787 788 /* Add to linked list */ 789 proxy_entry = malloc(sizeof(struct proxy_entry)); 790 if (proxy_entry == NULL) 791 return -1; 792 793 proxy_entry->proxy_type = proxy_type; 794 proxy_entry->rule_index = rule_index; 795 proxy_entry->proto = proto; 796 proxy_entry->proxy_port = htons(proxy_port); 797 proxy_entry->server_port = htons(server_port); 798 proxy_entry->server_addr = server_addr; 799 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr; 800 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr; 801 proxy_entry->src_mask = src_mask; 802 proxy_entry->dst_mask = dst_mask; 803 804 RuleAdd(proxy_entry); 805 806 return 0; 807 } 808