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