alias_proxy.c revision 45008
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 */ 58struct 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 87static 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 109static int IpMask(int, struct in_addr *); 110static int IpAddr(char *, struct in_addr *); 111static int IpPort(char *, int, int *); 112static void RuleAdd(struct proxy_entry *); 113static void RuleDelete(struct proxy_entry *); 114static int RuleNumberDelete(int); 115static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int); 116static void ProxyEncodeIpHeader(struct ip *, int); 117 118static int 119IpMask(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 135static int 136IpAddr(char *s, struct in_addr *addr) 137{ 138 if (inet_aton(s, addr) == 0) 139 return -1; 140 else 141 return 0; 142} 143 144static int 145IpPort(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 170void 171RuleAdd(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 216static void 217RuleDelete(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 230static int 231RuleNumberDelete(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 255static void 256ProxyEncodeTcpStream(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 341static void 342ProxyEncodeIpHeader(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#ifdef DEBUG 351 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip)); 352 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip)); 353#endif 354 355/* Check to see that there is room to add an IP option */ 356 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32)) 357 return; 358 359/* Build option and copy into packet */ 360 { 361 u_char *ptr; 362 struct tcphdr *tc; 363 364 ptr = (u_char *) pip; 365 ptr += 20; 366 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20); 367 368 option[0] = 0x64; /* class: 3 (reserved), option 4 */ 369 option[1] = OPTION_LEN_BYTES; 370 371 memcpy(&option[2], (u_char *) &pip->ip_dst, 4); 372 373 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 374 memcpy(&option[6], (u_char *) &tc->th_sport, 2); 375 376 memcpy(ptr, option, 8); 377 } 378 379/* Update checksum, header length and packet length */ 380 { 381 int i; 382 int accumulate; 383 u_short *sptr; 384 385 sptr = (u_short *) option; 386 accumulate = 0; 387 for (i=0; i<OPTION_LEN_INT16; i++) 388 accumulate -= *(sptr++); 389 390 sptr = (u_short *) pip; 391 accumulate += *sptr; 392 pip->ip_hl += OPTION_LEN_INT32; 393 accumulate -= *sptr; 394 395 accumulate += pip->ip_len; 396 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES); 397 accumulate -= pip->ip_len; 398 399 ADJUST_CHECKSUM(accumulate, pip->ip_sum); 400 } 401#undef OPTION_LEN_BYTES 402#undef OPTION_LEN_INT16 403#undef OPTION_LEN_INT32 404#ifdef DEBUG 405 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip)); 406 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip)); 407#endif 408} 409 410 411/* Functions by other packet alias source files 412 413 ProxyCheck() -- Checks whether an outgoing packet should 414 be proxied. 415 ProxyModify() -- Encodes the original destination address/port 416 for a packet which is to be redirected to 417 a proxy server. 418*/ 419 420int 421ProxyCheck(struct ip *pip, 422 struct in_addr *proxy_server_addr, 423 u_short *proxy_server_port) 424{ 425 u_short dst_port; 426 struct in_addr src_addr; 427 struct in_addr dst_addr; 428 struct proxy_entry *ptr; 429 430 src_addr = pip->ip_src; 431 dst_addr = pip->ip_dst; 432 dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2))) 433 ->th_dport; 434 435 ptr = proxyList; 436 while (ptr != NULL) 437 { 438 u_short proxy_port; 439 440 proxy_port = ptr->proxy_port; 441 if ((dst_port == proxy_port || proxy_port == 0) 442 && pip->ip_p == ptr->proto 443 && src_addr.s_addr != ptr->server_addr.s_addr) 444 { 445 struct in_addr src_addr_masked; 446 struct in_addr dst_addr_masked; 447 448 src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr; 449 dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr; 450 451 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr) 452 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) 453 { 454 if ((*proxy_server_port = ptr->server_port) == 0) 455 *proxy_server_port = dst_port; 456 *proxy_server_addr = ptr->server_addr; 457 return ptr->proxy_type; 458 } 459 } 460 ptr = ptr->next; 461 } 462 463 return 0; 464} 465 466void 467ProxyModify(struct alias_link *link, 468 struct ip *pip, 469 int maxpacketsize, 470 int proxy_type) 471{ 472 switch (proxy_type) 473 { 474 case PROXY_TYPE_ENCODE_IPHDR: 475 ProxyEncodeIpHeader(pip, maxpacketsize); 476 break; 477 478 case PROXY_TYPE_ENCODE_TCPSTREAM: 479 ProxyEncodeTcpStream(link, pip, maxpacketsize); 480 break; 481 } 482} 483 484 485/* 486 Public API functions 487*/ 488 489int 490PacketAliasProxyRule(const char *cmd) 491{ 492/* 493 * This function takes command strings of the form: 494 * 495 * server <addr>[:<port>] 496 * [port <port>] 497 * [rule n] 498 * [proto tcp|udp] 499 * [src <addr>[/n]] 500 * [dst <addr>[/n]] 501 * [type encode_tcp_stream|encode_ip_hdr|no_encode] 502 * 503 * delete <rule number> 504 * 505 * Subfields can be in arbitrary order. Port numbers and addresses 506 * must be in either numeric or symbolic form. An optional rule number 507 * is used to control the order in which rules are searched. If two 508 * rules have the same number, then search order cannot be guaranteed, 509 * and the rules should be disjoint. If no rule number is specified, 510 * then 0 is used, and group 0 rules are always checked before any 511 * others. 512 */ 513 int i, n, len; 514 int cmd_len; 515 int token_count; 516 int state; 517 char *token; 518 char buffer[256]; 519 char str_port[sizeof(buffer)]; 520 char str_server_port[sizeof(buffer)]; 521 522 int rule_index; 523 int proto; 524 int proxy_type; 525 int proxy_port; 526 int server_port; 527 struct in_addr server_addr; 528 struct in_addr src_addr, src_mask; 529 struct in_addr dst_addr, dst_mask; 530 struct proxy_entry *proxy_entry; 531 532/* Copy command line into a buffer */ 533 cmd_len = strlen(cmd); 534 if (cmd_len > (sizeof(buffer) - 1)) 535 return -1; 536 strcpy(buffer, cmd); 537 538/* Convert to lower case */ 539 len = strlen(buffer); 540 for (i=0; i<len; i++) 541 buffer[i] = tolower(buffer[i]); 542 543/* Set default proxy type */ 544 545/* Set up default values */ 546 rule_index = 0; 547 proxy_type = PROXY_TYPE_ENCODE_NONE; 548 proto = IPPROTO_TCP; 549 proxy_port = 0; 550 server_addr.s_addr = 0; 551 server_port = 0; 552 src_addr.s_addr = 0; 553 IpMask(0, &src_mask); 554 dst_addr.s_addr = 0; 555 IpMask(0, &dst_mask); 556 557 str_port[0] = 0; 558 str_server_port[0] = 0; 559 560/* Parse command string with state machine */ 561#define STATE_READ_KEYWORD 0 562#define STATE_READ_TYPE 1 563#define STATE_READ_PORT 2 564#define STATE_READ_SERVER 3 565#define STATE_READ_RULE 4 566#define STATE_READ_DELETE 5 567#define STATE_READ_PROTO 6 568#define STATE_READ_SRC 7 569#define STATE_READ_DST 8 570 state = STATE_READ_KEYWORD; 571 token = strtok(buffer, " \t"); 572 token_count = 0; 573 while (token != NULL) 574 { 575 token_count++; 576 switch (state) 577 { 578 case STATE_READ_KEYWORD: 579 if (strcmp(token, "type") == 0) 580 state = STATE_READ_TYPE; 581 else if (strcmp(token, "port") == 0) 582 state = STATE_READ_PORT; 583 else if (strcmp(token, "server") == 0) 584 state = STATE_READ_SERVER; 585 else if (strcmp(token, "rule") == 0) 586 state = STATE_READ_RULE; 587 else if (strcmp(token, "delete") == 0) 588 state = STATE_READ_DELETE; 589 else if (strcmp(token, "proto") == 0) 590 state = STATE_READ_PROTO; 591 else if (strcmp(token, "src") == 0) 592 state = STATE_READ_SRC; 593 else if (strcmp(token, "dst") == 0) 594 state = STATE_READ_DST; 595 else 596 return -1; 597 break; 598 599 case STATE_READ_TYPE: 600 if (strcmp(token, "encode_ip_hdr") == 0) 601 proxy_type = PROXY_TYPE_ENCODE_IPHDR; 602 else if (strcmp(token, "encode_tcp_stream") == 0) 603 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM; 604 else if (strcmp(token, "no_encode") == 0) 605 proxy_type = PROXY_TYPE_ENCODE_NONE; 606 else 607 return -1; 608 state = STATE_READ_KEYWORD; 609 break; 610 611 case STATE_READ_PORT: 612 strcpy(str_port, token); 613 state = STATE_READ_KEYWORD; 614 break; 615 616 case STATE_READ_SERVER: 617 { 618 int err; 619 char *p; 620 char s[sizeof(buffer)]; 621 622 p = token; 623 while (*p != ':' && *p != 0) 624 p++; 625 626 if (*p != ':') 627 { 628 err = IpAddr(token, &server_addr); 629 if (err) 630 return -1; 631 } 632 else 633 { 634 *p = ' '; 635 636 n = sscanf(token, "%s %s", s, str_server_port); 637 if (n != 2) 638 return -1; 639 640 err = IpAddr(s, &server_addr); 641 if (err) 642 return -1; 643 } 644 } 645 state = STATE_READ_KEYWORD; 646 break; 647 648 case STATE_READ_RULE: 649 n = sscanf(token, "%d", &rule_index); 650 if (n != 1 || rule_index < 0) 651 return -1; 652 state = STATE_READ_KEYWORD; 653 break; 654 655 case STATE_READ_DELETE: 656 { 657 int err; 658 int rule_to_delete; 659 660 if (token_count != 2) 661 return -1; 662 663 n = sscanf(token, "%d", &rule_to_delete); 664 if (n != 1) 665 return -1; 666 err = RuleNumberDelete(rule_to_delete); 667 if (err) 668 return -1; 669 return 0; 670 } 671 672 case STATE_READ_PROTO: 673 if (strcmp(token, "tcp") == 0) 674 proto = IPPROTO_TCP; 675 else if (strcmp(token, "udp") == 0) 676 proto = IPPROTO_UDP; 677 else 678 return -1; 679 state = STATE_READ_KEYWORD; 680 break; 681 682 case STATE_READ_SRC: 683 case STATE_READ_DST: 684 { 685 int err; 686 char *p; 687 struct in_addr mask; 688 struct in_addr addr; 689 690 p = token; 691 while (*p != '/' && *p != 0) 692 p++; 693 694 if (*p != '/') 695 { 696 IpMask(32, &mask); 697 err = IpAddr(token, &addr); 698 if (err) 699 return -1; 700 } 701 else 702 { 703 int n; 704 int nbits; 705 char s[sizeof(buffer)]; 706 707 *p = ' '; 708 n = sscanf(token, "%s %d", s, &nbits); 709 if (n != 2) 710 return -1; 711 712 err = IpAddr(s, &addr); 713 if (err) 714 return -1; 715 716 err = IpMask(nbits, &mask); 717 if (err) 718 return -1; 719 } 720 721 if (state == STATE_READ_SRC) 722 { 723 src_addr = addr; 724 src_mask = mask; 725 } 726 else 727 { 728 dst_addr = addr; 729 dst_mask = mask; 730 } 731 } 732 state = STATE_READ_KEYWORD; 733 break; 734 735 default: 736 return -1; 737 break; 738 } 739 740 token = strtok(NULL, " \t"); 741 } 742#undef STATE_READ_KEYWORD 743#undef STATE_READ_TYPE 744#undef STATE_READ_PORT 745#undef STATE_READ_SERVER 746#undef STATE_READ_RULE 747#undef STATE_READ_DELETE 748#undef STATE_READ_PROTO 749#undef STATE_READ_SRC 750#undef STATE_READ_DST 751 752/* Convert port strings to numbers. This needs to be done after 753 the string is parsed, because the prototype might not be designated 754 before the ports (which might be symbolic entries in /etc/services) */ 755 756 if (strlen(str_port) != 0) 757 { 758 int err; 759 760 err = IpPort(str_port, proto, &proxy_port); 761 if (err) 762 return -1; 763 } 764 else 765 { 766 proxy_port = 0; 767 } 768 769 if (strlen(str_server_port) != 0) 770 { 771 int err; 772 773 err = IpPort(str_server_port, proto, &server_port); 774 if (err) 775 return -1; 776 } 777 else 778 { 779 server_port = 0; 780 } 781 782/* Check that at least the server address has been defined */ 783 if (server_addr.s_addr == 0) 784 return -1; 785 786/* Add to linked list */ 787 proxy_entry = malloc(sizeof(struct proxy_entry)); 788 if (proxy_entry == NULL) 789 return -1; 790 791 proxy_entry->proxy_type = proxy_type; 792 proxy_entry->rule_index = rule_index; 793 proxy_entry->proto = proto; 794 proxy_entry->proxy_port = htons(proxy_port); 795 proxy_entry->server_port = htons(server_port); 796 proxy_entry->server_addr = server_addr; 797 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr; 798 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr; 799 proxy_entry->src_mask = src_mask; 800 proxy_entry->dst_mask = dst_mask; 801 802 RuleAdd(proxy_entry); 803 804 return 0; 805} 806