xref: /freebsd/sys/netinet/libalias/alias_db.c (revision 04c9749ff0148ec8f73b150cec8bc2c094a5d31a)
1 /*  -*- mode: c; tab-width: 8; c-basic-indent: 4; -*-
2     Alias_db.c encapsulates all data structures used for storing
3     packet aliasing data.  Other parts of the aliasing software
4     access data through functions provided in this file.
5 
6     Data storage is based on the notion of a "link", which is
7     established for ICMP echo/reply packets, UDP datagrams and
8     TCP stream connections.  A link stores the original source
9     and destination addresses.  For UDP and TCP, it also stores
10     source and destination port numbers, as well as an alias
11     port number.  Links are also used to store information about
12     fragments.
13 
14     There is a facility for sweeping through and deleting old
15     links as new packets are sent through.  A simple timeout is
16     used for ICMP and UDP links.  TCP links are left alone unless
17     there is an incomplete connection, in which case the link
18     can be deleted after a certain amount of time.
19 
20 
21     This software is placed into the public domain with no restrictions
22     on its distribution.
23 
24     Initial version: August, 1996  (cjm)
25 
26     Version 1.4: September 16, 1996 (cjm)
27         Facility for handling incoming links added.
28 
29     Version 1.6: September 18, 1996 (cjm)
30         ICMP data handling simplified.
31 
32     Version 1.7: January 9, 1997 (cjm)
33         Fragment handling simplified.
34         Saves pointers for unresolved fragments.
35         Permits links for unspecified remote ports
36           or unspecified remote addresses.
37         Fixed bug which did not properly zero port
38           table entries after a link was deleted.
39         Cleaned up some obsolete comments.
40 
41     Version 1.8: January 14, 1997 (cjm)
42         Fixed data type error in StartPoint().
43         (This error did not exist prior to v1.7
44         and was discovered and fixed by Ari Suutari)
45 
46     Version 1.9: February 1, 1997
47         Optionally, connections initiated from packet aliasing host
48         machine will will not have their port number aliased unless it
49         conflicts with an aliasing port already being used. (cjm)
50 
51         All options earlier being #ifdef'ed are now available through
52         a new interface, SetPacketAliasMode().  This allows run time
53         control (which is now available in PPP+pktAlias through the
54         'alias' keyword). (ee)
55 
56         Added ability to create an alias port without
57         either destination address or port specified.
58         port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee)
59 
60         Removed K&R style function headers
61         and general cleanup. (ee)
62 
63         Added packetAliasMode to replace compiler #defines's (ee)
64 
65         Allocates sockets for partially specified
66         ports if ALIAS_USE_SOCKETS defined. (cjm)
67 
68     Version 2.0: March, 1997
69         SetAliasAddress() will now clean up alias links
70         if the aliasing address is changed. (cjm)
71 
72         PacketAliasPermanentLink() function added to support permanent
73         links.  (J. Fortes suggested the need for this.)
74         Examples:
75 
76         (192.168.0.1, port 23)  <-> alias port 6002, unknown dest addr/port
77 
78         (192.168.0.2, port 21)  <-> alias port 3604, known dest addr
79                                                      unknown dest port
80 
81         These permanent links allow for incoming connections to
82         machines on the local network.  They can be given with a
83         user-chosen amount of specificity, with increasing specificity
84         meaning more security. (cjm)
85 
86         Quite a bit of rework to the basic engine.  The portTable[]
87         array, which kept track of which ports were in use was replaced
88         by a table/linked list structure. (cjm)
89 
90         SetExpire() function added. (cjm)
91 
92         DeleteLink() no longer frees memory association with a pointer
93         to a fragment (this bug was first recognized by E. Eklund in
94         v1.9).
95 
96     Version 2.1: May, 1997 (cjm)
97         Packet aliasing engine reworked so that it can handle
98         multiple external addresses rather than just a single
99         host address.
100 
101         PacketAliasRedirectPort() and PacketAliasRedirectAddr()
102         added to the API.  The first function is a more generalized
103         version of PacketAliasPermanentLink().  The second function
104         implements static network address translation.
105 
106     Version 3.2: July, 2000 (salander and satoh)
107         Added FindNewPortGroup to get contiguous range of port values.
108 
109         Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing
110 	link but not actually add one.
111 
112         Added FindRtspOut, which is closely derived from FindUdpTcpOut,
113 	except that the alias port (from FindNewPortGroup) is provided
114 	as input.
115 
116     See HISTORY file for additional revisions.
117 
118     $FreeBSD$
119 */
120 
121 
122 /* System include files */
123 #include <errno.h>
124 #include <stdlib.h>
125 #include <stdio.h>
126 #include <unistd.h>
127 
128 #include <sys/queue.h>
129 #include <sys/socket.h>
130 #include <sys/time.h>
131 #include <sys/types.h>
132 
133 /* BSD network include files */
134 #include <netinet/in_systm.h>
135 #include <netinet/in.h>
136 #include <netinet/ip.h>
137 #include <netinet/tcp.h>
138 #include <arpa/inet.h>
139 
140 #include "alias.h"
141 #include "alias_local.h"
142 
143 
144 
145 /*
146    Constants (note: constants are also defined
147               near relevant functions or structs)
148 */
149 
150 /* Sizes of input and output link tables */
151 #define LINK_TABLE_OUT_SIZE         101
152 #define LINK_TABLE_IN_SIZE         4001
153 
154 /* Parameters used for cleanup of expired links */
155 #define ALIAS_CLEANUP_INTERVAL_SECS  60
156 #define ALIAS_CLEANUP_MAX_SPOKES     30
157 
158 /* Timeouts (in seconds) for different link types */
159 #define ICMP_EXPIRE_TIME             60
160 #define UDP_EXPIRE_TIME              60
161 #define PPTP_EXPIRE_TIME             60
162 #define PROTO_EXPIRE_TIME            60
163 #define FRAGMENT_ID_EXPIRE_TIME      10
164 #define FRAGMENT_PTR_EXPIRE_TIME     30
165 
166 /* TCP link expire time for different cases */
167 /* When the link has been used and closed - minimal grace time to
168    allow ACKs and potential re-connect in FTP (XXX - is this allowed?)  */
169 #ifndef TCP_EXPIRE_DEAD
170 #   define TCP_EXPIRE_DEAD           10
171 #endif
172 
173 /* When the link has been used and closed on one side - the other side
174    is allowed to still send data */
175 #ifndef TCP_EXPIRE_SINGLEDEAD
176 #   define TCP_EXPIRE_SINGLEDEAD     90
177 #endif
178 
179 /* When the link isn't yet up */
180 #ifndef TCP_EXPIRE_INITIAL
181 #   define TCP_EXPIRE_INITIAL       300
182 #endif
183 
184 /* When the link is up */
185 #ifndef TCP_EXPIRE_CONNECTED
186 #   define TCP_EXPIRE_CONNECTED   86400
187 #endif
188 
189 
190 /* Dummy port number codes used for FindLinkIn/Out() and AddLink().
191    These constants can be anything except zero, which indicates an
192    unknown port number. */
193 
194 #define NO_DEST_PORT     1
195 #define NO_SRC_PORT      1
196 
197 
198 
199 /* Data Structures
200 
201     The fundamental data structure used in this program is
202     "struct alias_link".  Whenever a TCP connection is made,
203     a UDP datagram is sent out, or an ICMP echo request is made,
204     a link record is made (if it has not already been created).
205     The link record is identified by the source address/port
206     and the destination address/port. In the case of an ICMP
207     echo request, the source port is treated as being equivalent
208     with the 16-bit ID number of the ICMP packet.
209 
210     The link record also can store some auxiliary data.  For
211     TCP connections that have had sequence and acknowledgment
212     modifications, data space is available to track these changes.
213     A state field is used to keep track in changes to the TCP
214     connection state.  ID numbers of fragments can also be
215     stored in the auxiliary space.  Pointers to unresolved
216     fragments can also be stored.
217 
218     The link records support two independent chainings.  Lookup
219     tables for input and out tables hold the initial pointers
220     the link chains.  On input, the lookup table indexes on alias
221     port and link type.  On output, the lookup table indexes on
222     source address, destination address, source port, destination
223     port and link type.
224 */
225 
226 struct ack_data_record     /* used to save changes to ACK/sequence numbers */
227 {
228     u_long ack_old;
229     u_long ack_new;
230     int delta;
231     int active;
232 };
233 
234 struct tcp_state           /* Information about TCP connection        */
235 {
236     int in;                /* State for outside -> inside             */
237     int out;               /* State for inside  -> outside            */
238     int index;             /* Index to ACK data array                 */
239     int ack_modified;      /* Indicates whether ACK and sequence numbers */
240                            /* been modified                           */
241 };
242 
243 #define N_LINK_TCP_DATA   3 /* Number of distinct ACK number changes
244                                saved for a modified TCP stream */
245 struct tcp_dat
246 {
247     struct tcp_state state;
248     struct ack_data_record ack[N_LINK_TCP_DATA];
249     int fwhole;             /* Which firewall record is used for this hole? */
250 };
251 
252 struct server              /* LSNAT server pool (circular list) */
253 {
254     struct in_addr addr;
255     u_short port;
256     struct server *next;
257 };
258 
259 struct alias_link                /* Main data structure */
260 {
261     struct in_addr src_addr;     /* Address and port information        */
262     struct in_addr dst_addr;
263     struct in_addr alias_addr;
264     struct in_addr proxy_addr;
265     u_short src_port;
266     u_short dst_port;
267     u_short alias_port;
268     u_short proxy_port;
269     struct server *server;
270 
271     int link_type;               /* Type of link: TCP, UDP, ICMP, proto, frag */
272 
273 /* values for link_type */
274 #define LINK_ICMP                     IPPROTO_ICMP
275 #define LINK_UDP                      IPPROTO_UDP
276 #define LINK_TCP                      IPPROTO_TCP
277 #define LINK_FRAGMENT_ID              (IPPROTO_MAX + 1)
278 #define LINK_FRAGMENT_PTR             (IPPROTO_MAX + 2)
279 #define LINK_ADDR                     (IPPROTO_MAX + 3)
280 #define LINK_PPTP                     (IPPROTO_MAX + 4)
281 
282     int flags;                   /* indicates special characteristics   */
283 
284 /* flag bits */
285 #define LINK_UNKNOWN_DEST_PORT     0x01
286 #define LINK_UNKNOWN_DEST_ADDR     0x02
287 #define LINK_PERMANENT             0x04
288 #define LINK_PARTIALLY_SPECIFIED   0x03 /* logical-or of first two bits */
289 #define LINK_UNFIREWALLED          0x08
290 #define LINK_LAST_LINE_CRLF_TERMED 0x10
291 
292     int timestamp;               /* Time link was last accessed         */
293     int expire_time;             /* Expire time for link                */
294 
295     int sockfd;                  /* socket descriptor                   */
296 
297     LIST_ENTRY(alias_link) list_out; /* Linked list of pointers for     */
298     LIST_ENTRY(alias_link) list_in;  /* input and output lookup tables  */
299 
300     union                        /* Auxiliary data                      */
301     {
302         char *frag_ptr;
303         struct in_addr frag_addr;
304         struct tcp_dat *tcp;
305     } data;
306 };
307 
308 
309 
310 
311 
312 /* Global Variables
313 
314     The global variables listed here are only accessed from
315     within alias_db.c and so are prefixed with the static
316     designation.
317 */
318 
319 int packetAliasMode;                 /* Mode flags                      */
320                                      /*        - documented in alias.h  */
321 
322 static struct in_addr aliasAddress;  /* Address written onto source     */
323                                      /*   field of IP packet.           */
324 
325 static struct in_addr targetAddress; /* IP address incoming packets     */
326                                      /*   are sent to if no aliasing    */
327                                      /*   link already exists           */
328 
329 static struct in_addr nullAddress;   /* Used as a dummy parameter for   */
330                                      /*   some function calls           */
331 static LIST_HEAD(, alias_link)
332 linkTableOut[LINK_TABLE_OUT_SIZE];   /* Lookup table of pointers to     */
333                                      /*   chains of link records. Each  */
334 static LIST_HEAD(, alias_link)       /*   link record is doubly indexed */
335 linkTableIn[LINK_TABLE_IN_SIZE];     /*   into input and output lookup  */
336                                      /*   tables.                       */
337 
338 static int icmpLinkCount;            /* Link statistics                 */
339 static int udpLinkCount;
340 static int tcpLinkCount;
341 static int pptpLinkCount;
342 static int protoLinkCount;
343 static int fragmentIdLinkCount;
344 static int fragmentPtrLinkCount;
345 static int sockCount;
346 
347 static int cleanupIndex;             /* Index to chain of link table    */
348                                      /* being inspected for old links   */
349 
350 static int timeStamp;                /* System time in seconds for      */
351                                      /* current packet                  */
352 
353 static int lastCleanupTime;          /* Last time IncrementalCleanup()  */
354                                      /* was called                      */
355 
356 static int houseKeepingResidual;     /* used by HouseKeeping()          */
357 
358 static int deleteAllLinks;           /* If equal to zero, DeleteLink()  */
359                                      /* will not remove permanent links */
360 
361 static FILE *monitorFile;            /* File descriptor for link        */
362                                      /* statistics monitoring file      */
363 
364 static int newDefaultLink;           /* Indicates if a new aliasing     */
365                                      /* link has been created after a   */
366                                      /* call to PacketAliasIn/Out().    */
367 
368 #ifndef NO_FW_PUNCH
369 static int fireWallFD = -1;          /* File descriptor to be able to   */
370                                      /* control firewall.  Opened by    */
371                                      /* PacketAliasSetMode on first     */
372                                      /* setting the PKT_ALIAS_PUNCH_FW  */
373                                      /* flag.                           */
374 #endif
375 
376 
377 
378 
379 
380 
381 
382 /* Internal utility routines (used only in alias_db.c)
383 
384 Lookup table starting points:
385     StartPointIn()           -- link table initial search point for
386                                 incoming packets
387     StartPointOut()          -- link table initial search point for
388                                 outgoing packets
389 
390 Miscellaneous:
391     SeqDiff()                -- difference between two TCP sequences
392     ShowAliasStats()         -- send alias statistics to a monitor file
393 */
394 
395 
396 /* Local prototypes */
397 static u_int StartPointIn(struct in_addr, u_short, int);
398 
399 static u_int StartPointOut(struct in_addr, struct in_addr,
400                            u_short, u_short, int);
401 
402 static int SeqDiff(u_long, u_long);
403 
404 static void ShowAliasStats(void);
405 
406 #ifndef NO_FW_PUNCH
407 /* Firewall control */
408 static void InitPunchFW(void);
409 static void UninitPunchFW(void);
410 static void ClearFWHole(struct alias_link *link);
411 #endif
412 
413 /* Log file control */
414 static void InitPacketAliasLog(void);
415 static void UninitPacketAliasLog(void);
416 
417 static u_int
418 StartPointIn(struct in_addr alias_addr,
419              u_short alias_port,
420              int link_type)
421 {
422     u_int n;
423 
424     n  = alias_addr.s_addr;
425     n += alias_port;
426     n += link_type;
427     return(n % LINK_TABLE_IN_SIZE);
428 }
429 
430 
431 static u_int
432 StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
433               u_short src_port, u_short dst_port, int link_type)
434 {
435     u_int n;
436 
437     n  = src_addr.s_addr;
438     n += dst_addr.s_addr;
439     n += src_port;
440     n += dst_port;
441     n += link_type;
442 
443     return(n % LINK_TABLE_OUT_SIZE);
444 }
445 
446 
447 static int
448 SeqDiff(u_long x, u_long y)
449 {
450 /* Return the difference between two TCP sequence numbers */
451 
452 /*
453     This function is encapsulated in case there are any unusual
454     arithmetic conditions that need to be considered.
455 */
456 
457     return (ntohl(y) - ntohl(x));
458 }
459 
460 
461 static void
462 ShowAliasStats(void)
463 {
464 /* Used for debugging */
465 
466    if (monitorFile)
467    {
468       fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, pptp=%d, proto=%d, frag_id=%d frag_ptr=%d",
469               icmpLinkCount,
470               udpLinkCount,
471               tcpLinkCount,
472               pptpLinkCount,
473               protoLinkCount,
474               fragmentIdLinkCount,
475               fragmentPtrLinkCount);
476 
477       fprintf(monitorFile, " / tot=%d  (sock=%d)\n",
478               icmpLinkCount + udpLinkCount
479                             + tcpLinkCount
480                             + pptpLinkCount
481                             + protoLinkCount
482                             + fragmentIdLinkCount
483                             + fragmentPtrLinkCount,
484               sockCount);
485 
486       fflush(monitorFile);
487    }
488 }
489 
490 
491 
492 
493 
494 /* Internal routines for finding, deleting and adding links
495 
496 Port Allocation:
497     GetNewPort()             -- find and reserve new alias port number
498     GetSocket()              -- try to allocate a socket for a given port
499 
500 Link creation and deletion:
501     CleanupAliasData()      - remove all link chains from lookup table
502     IncrementalCleanup()    - look for stale links in a single chain
503     DeleteLink()            - remove link
504     AddLink()               - add link
505     ReLink()                - change link
506 
507 Link search:
508     FindLinkOut()           - find link for outgoing packets
509     FindLinkIn()            - find link for incoming packets
510 
511 Port search:
512     FindNewPortGroup()      - find an available group of ports
513 */
514 
515 /* Local prototypes */
516 static int GetNewPort(struct alias_link *, int);
517 
518 static u_short GetSocket(u_short, int *, int);
519 
520 static void CleanupAliasData(void);
521 
522 static void IncrementalCleanup(void);
523 
524 static void DeleteLink(struct alias_link *);
525 
526 static struct alias_link *
527 AddLink(struct in_addr, struct in_addr, struct in_addr,
528         u_short, u_short, int, int);
529 
530 static struct alias_link *
531 ReLink(struct alias_link *,
532        struct in_addr, struct in_addr, struct in_addr,
533         u_short, u_short, int, int);
534 
535 static struct alias_link *
536 FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int, int);
537 
538 static struct alias_link *
539 FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int);
540 
541 
542 #define ALIAS_PORT_BASE            0x08000
543 #define ALIAS_PORT_MASK            0x07fff
544 #define ALIAS_PORT_MASK_EVEN       0x07ffe
545 #define GET_NEW_PORT_MAX_ATTEMPTS       20
546 
547 #define GET_ALIAS_PORT                  -1
548 #define GET_ALIAS_ID        GET_ALIAS_PORT
549 
550 #define FIND_EVEN_ALIAS_BASE             1
551 
552 /* GetNewPort() allocates port numbers.  Note that if a port number
553    is already in use, that does not mean that it cannot be used by
554    another link concurrently.  This is because GetNewPort() looks for
555    unused triplets: (dest addr, dest port, alias port). */
556 
557 static int
558 GetNewPort(struct alias_link *link, int alias_port_param)
559 {
560     int i;
561     int max_trials;
562     u_short port_sys;
563     u_short port_net;
564 
565 /*
566    Description of alias_port_param for GetNewPort().  When
567    this parameter is zero or positive, it precisely specifies
568    the port number.  GetNewPort() will return this number
569    without check that it is in use.
570 
571    When this parameter is GET_ALIAS_PORT, it indicates to get a randomly
572    selected port number.
573 */
574 
575     if (alias_port_param == GET_ALIAS_PORT)
576     {
577         /*
578          * The aliasing port is automatically selected
579          * by one of two methods below:
580          */
581         max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
582 
583         if (packetAliasMode & PKT_ALIAS_SAME_PORTS)
584         {
585             /*
586              * When the PKT_ALIAS_SAME_PORTS option is
587              * chosen, the first try will be the
588              * actual source port. If this is already
589              * in use, the remainder of the trials
590              * will be random.
591              */
592             port_net = link->src_port;
593             port_sys = ntohs(port_net);
594         }
595         else
596         {
597             /* First trial and all subsequent are random. */
598             port_sys = random() & ALIAS_PORT_MASK;
599             port_sys += ALIAS_PORT_BASE;
600             port_net = htons(port_sys);
601         }
602     }
603     else if (alias_port_param >= 0 && alias_port_param < 0x10000)
604     {
605         link->alias_port = (u_short) alias_port_param;
606         return(0);
607     }
608     else
609     {
610 #ifdef DEBUG
611         fprintf(stderr, "PacketAlias/GetNewPort(): ");
612         fprintf(stderr, "input parameter error\n");
613 #endif
614         return(-1);
615     }
616 
617 
618 /* Port number search */
619     for (i=0; i<max_trials; i++)
620     {
621         int go_ahead;
622         struct alias_link *search_result;
623 
624         search_result = FindLinkIn(link->dst_addr, link->alias_addr,
625                                    link->dst_port, port_net,
626                                    link->link_type, 0);
627 
628         if (search_result == NULL)
629             go_ahead = 1;
630         else if (!(link->flags          & LINK_PARTIALLY_SPECIFIED)
631                && (search_result->flags & LINK_PARTIALLY_SPECIFIED))
632             go_ahead = 1;
633         else
634             go_ahead = 0;
635 
636         if (go_ahead)
637         {
638             if ((packetAliasMode & PKT_ALIAS_USE_SOCKETS)
639              && (link->flags & LINK_PARTIALLY_SPECIFIED)
640 	     && ((link->link_type == LINK_TCP) ||
641 		 (link->link_type == LINK_UDP)))
642             {
643                 if (GetSocket(port_net, &link->sockfd, link->link_type))
644                 {
645                     link->alias_port = port_net;
646                     return(0);
647                 }
648             }
649             else
650             {
651                 link->alias_port = port_net;
652                 return(0);
653             }
654         }
655 
656         port_sys = random() & ALIAS_PORT_MASK;
657         port_sys += ALIAS_PORT_BASE;
658         port_net = htons(port_sys);
659     }
660 
661 #ifdef DEBUG
662     fprintf(stderr, "PacketAlias/GetnewPort(): ");
663     fprintf(stderr, "could not find free port\n");
664 #endif
665 
666     return(-1);
667 }
668 
669 
670 static u_short
671 GetSocket(u_short port_net, int *sockfd, int link_type)
672 {
673     int err;
674     int sock;
675     struct sockaddr_in sock_addr;
676 
677     if (link_type == LINK_TCP)
678         sock = socket(AF_INET, SOCK_STREAM, 0);
679     else if (link_type == LINK_UDP)
680         sock = socket(AF_INET, SOCK_DGRAM, 0);
681     else
682     {
683 #ifdef DEBUG
684         fprintf(stderr, "PacketAlias/GetSocket(): ");
685         fprintf(stderr, "incorrect link type\n");
686 #endif
687         return(0);
688     }
689 
690     if (sock < 0)
691     {
692 #ifdef DEBUG
693         fprintf(stderr, "PacketAlias/GetSocket(): ");
694         fprintf(stderr, "socket() error %d\n", *sockfd);
695 #endif
696         return(0);
697     }
698 
699     sock_addr.sin_family = AF_INET;
700     sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
701     sock_addr.sin_port = port_net;
702 
703     err = bind(sock,
704                (struct sockaddr *) &sock_addr,
705                sizeof(sock_addr));
706     if (err == 0)
707     {
708         sockCount++;
709         *sockfd = sock;
710         return(1);
711     }
712     else
713     {
714         close(sock);
715         return(0);
716     }
717 }
718 
719 
720 /* FindNewPortGroup() returns a base port number for an available
721    range of contiguous port numbers. Note that if a port number
722    is already in use, that does not mean that it cannot be used by
723    another link concurrently.  This is because FindNewPortGroup()
724    looks for unused triplets: (dest addr, dest port, alias port). */
725 
726 int
727 FindNewPortGroup(struct in_addr  dst_addr,
728                  struct in_addr  alias_addr,
729                  u_short         src_port,
730                  u_short         dst_port,
731                  u_short         port_count,
732 		 u_char          proto,
733 		 u_char          align)
734 {
735     int     i, j;
736     int     max_trials;
737     u_short port_sys;
738     int     link_type;
739 
740     /*
741      * Get link_type from protocol
742      */
743 
744     switch (proto)
745     {
746     case IPPROTO_UDP:
747         link_type = LINK_UDP;
748         break;
749     case IPPROTO_TCP:
750         link_type = LINK_TCP;
751         break;
752     default:
753         return (0);
754         break;
755     }
756 
757     /*
758      * The aliasing port is automatically selected
759      * by one of two methods below:
760      */
761     max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
762 
763     if (packetAliasMode & PKT_ALIAS_SAME_PORTS) {
764       /*
765        * When the ALIAS_SAME_PORTS option is
766        * chosen, the first try will be the
767        * actual source port. If this is already
768        * in use, the remainder of the trials
769        * will be random.
770        */
771       port_sys = ntohs(src_port);
772 
773     } else {
774 
775       /* First trial and all subsequent are random. */
776       if (align == FIND_EVEN_ALIAS_BASE)
777         port_sys = random() & ALIAS_PORT_MASK_EVEN;
778       else
779         port_sys = random() & ALIAS_PORT_MASK;
780 
781       port_sys += ALIAS_PORT_BASE;
782     }
783 
784 /* Port number search */
785     for (i = 0; i < max_trials; i++) {
786 
787       struct alias_link *search_result;
788 
789       for (j = 0; j < port_count; j++)
790         if (0 != (search_result = FindLinkIn(dst_addr, alias_addr,
791                                         dst_port, htons(port_sys + j),
792                                         link_type, 0)))
793 	  break;
794 
795       /* Found a good range, return base */
796       if (j == port_count)
797 	return (htons(port_sys));
798 
799       /* Find a new base to try */
800       if (align == FIND_EVEN_ALIAS_BASE)
801         port_sys = random() & ALIAS_PORT_MASK_EVEN;
802       else
803         port_sys = random() & ALIAS_PORT_MASK;
804 
805       port_sys += ALIAS_PORT_BASE;
806     }
807 
808 #ifdef DEBUG
809     fprintf(stderr, "PacketAlias/FindNewPortGroup(): ");
810     fprintf(stderr, "could not find free port(s)\n");
811 #endif
812 
813     return(0);
814 }
815 
816 static void
817 CleanupAliasData(void)
818 {
819     struct alias_link *link;
820     int i, icount;
821 
822     icount = 0;
823     for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
824     {
825         link = LIST_FIRST(&linkTableOut[i]);
826         while (link != NULL)
827         {
828             struct alias_link *link_next;
829             link_next = LIST_NEXT(link, list_out);
830             icount++;
831             DeleteLink(link);
832             link = link_next;
833         }
834     }
835 
836     cleanupIndex =0;
837 }
838 
839 
840 static void
841 IncrementalCleanup(void)
842 {
843     int icount;
844     struct alias_link *link;
845 
846     icount = 0;
847     link = LIST_FIRST(&linkTableOut[cleanupIndex++]);
848     while (link != NULL)
849     {
850         int idelta;
851         struct alias_link *link_next;
852 
853         link_next = LIST_NEXT(link, list_out);
854         idelta = timeStamp - link->timestamp;
855         switch (link->link_type)
856         {
857             case LINK_TCP:
858                 if (idelta > link->expire_time)
859                 {
860                     struct tcp_dat *tcp_aux;
861 
862                     tcp_aux = link->data.tcp;
863                     if (tcp_aux->state.in  != ALIAS_TCP_STATE_CONNECTED
864                      || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED)
865                     {
866                         DeleteLink(link);
867                         icount++;
868                     }
869                 }
870                 break;
871             default:
872                 if (idelta > link->expire_time)
873                 {
874                     DeleteLink(link);
875                     icount++;
876                 }
877                 break;
878         }
879         link = link_next;
880     }
881 
882     if (cleanupIndex == LINK_TABLE_OUT_SIZE)
883         cleanupIndex = 0;
884 }
885 
886 static void
887 DeleteLink(struct alias_link *link)
888 {
889 
890 /* Don't do anything if the link is marked permanent */
891     if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT)
892         return;
893 
894 #ifndef NO_FW_PUNCH
895 /* Delete associated firewall hole, if any */
896     ClearFWHole(link);
897 #endif
898 
899 /* Free memory allocated for LSNAT server pool */
900     if (link->server != NULL) {
901 	struct server *head, *curr, *next;
902 
903 	head = curr = link->server;
904 	do {
905 	    next = curr->next;
906 	    free(curr);
907 	} while ((curr = next) != head);
908     }
909 
910 /* Adjust output table pointers */
911     LIST_REMOVE(link, list_out);
912 
913 /* Adjust input table pointers */
914     LIST_REMOVE(link, list_in);
915 
916 /* Close socket, if one has been allocated */
917     if (link->sockfd != -1)
918     {
919         sockCount--;
920         close(link->sockfd);
921     }
922 
923 /* Link-type dependent cleanup */
924     switch(link->link_type)
925     {
926         case LINK_ICMP:
927             icmpLinkCount--;
928             break;
929         case LINK_UDP:
930             udpLinkCount--;
931             break;
932         case LINK_TCP:
933             tcpLinkCount--;
934             if (link->data.tcp != NULL)
935                 free(link->data.tcp);
936             break;
937         case LINK_PPTP:
938             pptpLinkCount--;
939             break;
940         case LINK_FRAGMENT_ID:
941             fragmentIdLinkCount--;
942             break;
943         case LINK_FRAGMENT_PTR:
944             fragmentPtrLinkCount--;
945             if (link->data.frag_ptr != NULL)
946                 free(link->data.frag_ptr);
947             break;
948 	case LINK_ADDR:
949 	    break;
950         default:
951             protoLinkCount--;
952             break;
953     }
954 
955 /* Free memory */
956     free(link);
957 
958 /* Write statistics, if logging enabled */
959     if (packetAliasMode & PKT_ALIAS_LOG)
960     {
961         ShowAliasStats();
962     }
963 }
964 
965 
966 static struct alias_link *
967 AddLink(struct in_addr  src_addr,
968         struct in_addr  dst_addr,
969         struct in_addr  alias_addr,
970         u_short         src_port,
971         u_short         dst_port,
972         int             alias_port_param,  /* if less than zero, alias   */
973         int             link_type)         /* port will be automatically */
974 {                                          /* chosen. If greater than    */
975     u_int start_point;                     /* zero, equal to alias port  */
976     struct alias_link *link;
977 
978     link = malloc(sizeof(struct alias_link));
979     if (link != NULL)
980     {
981     /* Basic initialization */
982         link->src_addr          = src_addr;
983         link->dst_addr          = dst_addr;
984         link->alias_addr        = alias_addr;
985         link->proxy_addr.s_addr = INADDR_ANY;
986         link->src_port          = src_port;
987         link->dst_port          = dst_port;
988         link->proxy_port        = 0;
989         link->server            = NULL;
990         link->link_type         = link_type;
991         link->sockfd            = -1;
992         link->flags             = 0;
993         link->timestamp         = timeStamp;
994 
995     /* Expiration time */
996         switch (link_type)
997         {
998         case LINK_ICMP:
999             link->expire_time = ICMP_EXPIRE_TIME;
1000             break;
1001         case LINK_UDP:
1002             link->expire_time = UDP_EXPIRE_TIME;
1003             break;
1004         case LINK_TCP:
1005             link->expire_time = TCP_EXPIRE_INITIAL;
1006             break;
1007         case LINK_PPTP:
1008             link->expire_time = PPTP_EXPIRE_TIME;
1009             break;
1010         case LINK_FRAGMENT_ID:
1011             link->expire_time = FRAGMENT_ID_EXPIRE_TIME;
1012             break;
1013         case LINK_FRAGMENT_PTR:
1014             link->expire_time = FRAGMENT_PTR_EXPIRE_TIME;
1015             break;
1016 	case LINK_ADDR:
1017 	    break;
1018         default:
1019             link->expire_time = PROTO_EXPIRE_TIME;
1020             break;
1021         }
1022 
1023     /* Determine alias flags */
1024         if (dst_addr.s_addr == INADDR_ANY)
1025             link->flags |= LINK_UNKNOWN_DEST_ADDR;
1026         if (dst_port == 0)
1027             link->flags |= LINK_UNKNOWN_DEST_PORT;
1028 
1029     /* Determine alias port */
1030         if (GetNewPort(link, alias_port_param) != 0)
1031         {
1032             free(link);
1033             return(NULL);
1034         }
1035 
1036     /* Set up pointers for output lookup table */
1037         start_point = StartPointOut(src_addr, dst_addr,
1038                                     src_port, dst_port, link_type);
1039         LIST_INSERT_HEAD(&linkTableOut[start_point], link, list_out);
1040 
1041     /* Set up pointers for input lookup table */
1042         start_point = StartPointIn(alias_addr, link->alias_port, link_type);
1043         LIST_INSERT_HEAD(&linkTableIn[start_point], link, list_in);
1044 
1045     /* Link-type dependent initialization */
1046         switch(link_type)
1047         {
1048             struct tcp_dat  *aux_tcp;
1049 
1050             case LINK_ICMP:
1051                 icmpLinkCount++;
1052                 break;
1053             case LINK_UDP:
1054                 udpLinkCount++;
1055                 break;
1056             case LINK_TCP:
1057                 aux_tcp = malloc(sizeof(struct tcp_dat));
1058                 link->data.tcp = aux_tcp;
1059                 if (aux_tcp != NULL)
1060                 {
1061                     int i;
1062 
1063                     tcpLinkCount++;
1064                     aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
1065                     aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
1066                     aux_tcp->state.index = 0;
1067                     aux_tcp->state.ack_modified = 0;
1068                     for (i=0; i<N_LINK_TCP_DATA; i++)
1069                         aux_tcp->ack[i].active = 0;
1070                     aux_tcp->fwhole = -1;
1071                 }
1072                 else
1073                 {
1074 #ifdef DEBUG
1075                     fprintf(stderr, "PacketAlias/AddLink: ");
1076                     fprintf(stderr, " cannot allocate auxiliary TCP data\n");
1077 #endif
1078                 }
1079                 break;
1080             case LINK_PPTP:
1081                 pptpLinkCount++;
1082                 break;
1083             case LINK_FRAGMENT_ID:
1084                 fragmentIdLinkCount++;
1085                 break;
1086             case LINK_FRAGMENT_PTR:
1087                 fragmentPtrLinkCount++;
1088                 break;
1089 	    case LINK_ADDR:
1090 		break;
1091             default:
1092                 protoLinkCount++;
1093                 break;
1094         }
1095     }
1096     else
1097     {
1098 #ifdef DEBUG
1099         fprintf(stderr, "PacketAlias/AddLink(): ");
1100         fprintf(stderr, "malloc() call failed.\n");
1101 #endif
1102     }
1103 
1104     if (packetAliasMode & PKT_ALIAS_LOG)
1105     {
1106         ShowAliasStats();
1107     }
1108 
1109     return(link);
1110 }
1111 
1112 static struct alias_link *
1113 ReLink(struct alias_link *old_link,
1114        struct in_addr  src_addr,
1115        struct in_addr  dst_addr,
1116        struct in_addr  alias_addr,
1117        u_short         src_port,
1118        u_short         dst_port,
1119        int             alias_port_param,   /* if less than zero, alias   */
1120        int             link_type)          /* port will be automatically */
1121 {                                          /* chosen. If greater than    */
1122     struct alias_link *new_link;           /* zero, equal to alias port  */
1123 
1124     new_link = AddLink(src_addr, dst_addr, alias_addr,
1125                        src_port, dst_port, alias_port_param,
1126                        link_type);
1127 #ifndef NO_FW_PUNCH
1128     if (new_link != NULL &&
1129         old_link->link_type == LINK_TCP &&
1130         old_link->data.tcp &&
1131         old_link->data.tcp->fwhole > 0) {
1132       PunchFWHole(new_link);
1133     }
1134 #endif
1135     DeleteLink(old_link);
1136     return new_link;
1137 }
1138 
1139 static struct alias_link *
1140 _FindLinkOut(struct in_addr src_addr,
1141             struct in_addr dst_addr,
1142             u_short src_port,
1143             u_short dst_port,
1144             int link_type,
1145             int replace_partial_links)
1146 {
1147     u_int i;
1148     struct alias_link *link;
1149 
1150     i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type);
1151     LIST_FOREACH(link, &linkTableOut[i], list_out)
1152     {
1153         if (link->src_addr.s_addr == src_addr.s_addr
1154          && link->server          == NULL
1155          && link->dst_addr.s_addr == dst_addr.s_addr
1156          && link->dst_port        == dst_port
1157          && link->src_port        == src_port
1158          && link->link_type       == link_type)
1159         {
1160             link->timestamp = timeStamp;
1161             break;
1162         }
1163     }
1164 
1165 /* Search for partially specified links. */
1166     if (link == NULL && replace_partial_links)
1167     {
1168         if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY)
1169         {
1170             link = _FindLinkOut(src_addr, dst_addr, src_port, 0,
1171                                 link_type, 0);
1172             if (link == NULL)
1173                 link = _FindLinkOut(src_addr, nullAddress, src_port,
1174                                     dst_port, link_type, 0);
1175         }
1176         if (link == NULL &&
1177            (dst_port != 0 || dst_addr.s_addr != INADDR_ANY))
1178         {
1179             link = _FindLinkOut(src_addr, nullAddress, src_port, 0,
1180                                 link_type, 0);
1181         }
1182         if (link != NULL)
1183         {
1184             link = ReLink(link,
1185                           src_addr, dst_addr, link->alias_addr,
1186                           src_port, dst_port, link->alias_port,
1187                           link_type);
1188         }
1189     }
1190 
1191     return(link);
1192 }
1193 
1194 static struct alias_link *
1195 FindLinkOut(struct in_addr src_addr,
1196             struct in_addr dst_addr,
1197             u_short src_port,
1198             u_short dst_port,
1199             int link_type,
1200             int replace_partial_links)
1201 {
1202     struct alias_link *link;
1203 
1204     link = _FindLinkOut(src_addr, dst_addr, src_port, dst_port,
1205                         link_type, replace_partial_links);
1206 
1207     if (link == NULL)
1208     {
1209     /* The following allows permanent links to be
1210        specified as using the default source address
1211        (i.e. device interface address) without knowing
1212        in advance what that address is. */
1213         if (aliasAddress.s_addr != 0 &&
1214             src_addr.s_addr == aliasAddress.s_addr)
1215         {
1216             link = _FindLinkOut(nullAddress, dst_addr, src_port, dst_port,
1217                                link_type, replace_partial_links);
1218         }
1219     }
1220 
1221     return(link);
1222 }
1223 
1224 
1225 static struct alias_link *
1226 _FindLinkIn(struct in_addr dst_addr,
1227            struct in_addr  alias_addr,
1228            u_short         dst_port,
1229            u_short         alias_port,
1230            int             link_type,
1231            int             replace_partial_links)
1232 {
1233     int flags_in;
1234     u_int start_point;
1235     struct alias_link *link;
1236     struct alias_link *link_fully_specified;
1237     struct alias_link *link_unknown_all;
1238     struct alias_link *link_unknown_dst_addr;
1239     struct alias_link *link_unknown_dst_port;
1240 
1241 /* Initialize pointers */
1242     link_fully_specified  = NULL;
1243     link_unknown_all      = NULL;
1244     link_unknown_dst_addr = NULL;
1245     link_unknown_dst_port = NULL;
1246 
1247 /* If either the dest addr or port is unknown, the search
1248    loop will have to know about this. */
1249 
1250     flags_in = 0;
1251     if (dst_addr.s_addr == INADDR_ANY)
1252         flags_in |= LINK_UNKNOWN_DEST_ADDR;
1253     if (dst_port == 0)
1254         flags_in |= LINK_UNKNOWN_DEST_PORT;
1255 
1256 /* Search loop */
1257     start_point = StartPointIn(alias_addr, alias_port, link_type);
1258     LIST_FOREACH(link, &linkTableIn[start_point], list_in)
1259     {
1260         int flags;
1261 
1262         flags = flags_in | link->flags;
1263         if (!(flags & LINK_PARTIALLY_SPECIFIED))
1264         {
1265             if (link->alias_addr.s_addr == alias_addr.s_addr
1266              && link->alias_port        == alias_port
1267              && link->dst_addr.s_addr   == dst_addr.s_addr
1268              && link->dst_port          == dst_port
1269              && link->link_type         == link_type)
1270             {
1271                 link_fully_specified = link;
1272                 break;
1273             }
1274         }
1275         else if ((flags & LINK_UNKNOWN_DEST_ADDR)
1276               && (flags & LINK_UNKNOWN_DEST_PORT))
1277         {
1278             if (link->alias_addr.s_addr == alias_addr.s_addr
1279              && link->alias_port        == alias_port
1280              && link->link_type         == link_type)
1281             {
1282                 if (link_unknown_all == NULL)
1283                     link_unknown_all = link;
1284             }
1285         }
1286         else if (flags & LINK_UNKNOWN_DEST_ADDR)
1287         {
1288             if (link->alias_addr.s_addr == alias_addr.s_addr
1289              && link->alias_port        == alias_port
1290              && link->link_type         == link_type
1291              && link->dst_port          == dst_port)
1292             {
1293                 if (link_unknown_dst_addr == NULL)
1294                     link_unknown_dst_addr = link;
1295             }
1296         }
1297         else if (flags & LINK_UNKNOWN_DEST_PORT)
1298         {
1299             if (link->alias_addr.s_addr == alias_addr.s_addr
1300              && link->alias_port        == alias_port
1301              && link->link_type         == link_type
1302              && link->dst_addr.s_addr   == dst_addr.s_addr)
1303             {
1304                 if (link_unknown_dst_port == NULL)
1305                     link_unknown_dst_port = link;
1306             }
1307         }
1308     }
1309 
1310 
1311 
1312     if (link_fully_specified != NULL)
1313     {
1314         link_fully_specified->timestamp = timeStamp;
1315         link = link_fully_specified;
1316     }
1317     else if (link_unknown_dst_port != NULL)
1318 	link = link_unknown_dst_port;
1319     else if (link_unknown_dst_addr != NULL)
1320 	link = link_unknown_dst_addr;
1321     else if (link_unknown_all != NULL)
1322 	link = link_unknown_all;
1323     else
1324         return (NULL);
1325 
1326     if (replace_partial_links &&
1327 	(link->flags & LINK_PARTIALLY_SPECIFIED || link->server != NULL))
1328     {
1329 	struct in_addr src_addr;
1330 	u_short src_port;
1331 
1332 	if (link->server != NULL) {		/* LSNAT link */
1333 	    src_addr = link->server->addr;
1334 	    src_port = link->server->port;
1335 	    link->server = link->server->next;
1336 	} else {
1337 	    src_addr = link->src_addr;
1338 	    src_port = link->src_port;
1339 	}
1340 
1341 	link = ReLink(link,
1342 		      src_addr, dst_addr, alias_addr,
1343 		      src_port, dst_port, alias_port,
1344 		      link_type);
1345     }
1346 
1347     return (link);
1348 }
1349 
1350 static struct alias_link *
1351 FindLinkIn(struct in_addr dst_addr,
1352            struct in_addr alias_addr,
1353            u_short dst_port,
1354            u_short alias_port,
1355            int link_type,
1356            int replace_partial_links)
1357 {
1358     struct alias_link *link;
1359 
1360     link = _FindLinkIn(dst_addr, alias_addr, dst_port, alias_port,
1361                        link_type, replace_partial_links);
1362 
1363     if (link == NULL)
1364     {
1365     /* The following allows permanent links to be
1366        specified as using the default aliasing address
1367        (i.e. device interface address) without knowing
1368        in advance what that address is. */
1369         if (aliasAddress.s_addr != 0 &&
1370             alias_addr.s_addr == aliasAddress.s_addr)
1371         {
1372             link = _FindLinkIn(dst_addr, nullAddress, dst_port, alias_port,
1373                                link_type, replace_partial_links);
1374         }
1375     }
1376 
1377     return(link);
1378 }
1379 
1380 
1381 
1382 
1383 /* External routines for finding/adding links
1384 
1385 -- "external" means outside alias_db.c, but within alias*.c --
1386 
1387     FindIcmpIn(), FindIcmpOut()
1388     FindFragmentIn1(), FindFragmentIn2()
1389     AddFragmentPtrLink(), FindFragmentPtr()
1390     FindProtoIn(), FindProtoOut()
1391     FindUdpTcpIn(), FindUdpTcpOut()
1392     FindPptpIn(), FindPptpOut()
1393     FindOriginalAddress(), FindAliasAddress()
1394 
1395 (prototypes in alias_local.h)
1396 */
1397 
1398 
1399 struct alias_link *
1400 FindIcmpIn(struct in_addr dst_addr,
1401            struct in_addr alias_addr,
1402            u_short id_alias)
1403 {
1404     return FindLinkIn(dst_addr, alias_addr,
1405                       NO_DEST_PORT, id_alias,
1406                       LINK_ICMP, 0);
1407 }
1408 
1409 
1410 struct alias_link *
1411 FindIcmpOut(struct in_addr src_addr,
1412             struct in_addr dst_addr,
1413             u_short id)
1414 {
1415     struct alias_link * link;
1416 
1417     link = FindLinkOut(src_addr, dst_addr,
1418                        id, NO_DEST_PORT,
1419                        LINK_ICMP, 0);
1420     if (link == NULL)
1421     {
1422         struct in_addr alias_addr;
1423 
1424         alias_addr = FindAliasAddress(src_addr);
1425         link = AddLink(src_addr, dst_addr, alias_addr,
1426                        id, NO_DEST_PORT, GET_ALIAS_ID,
1427                        LINK_ICMP);
1428     }
1429 
1430     return(link);
1431 }
1432 
1433 
1434 struct alias_link *
1435 FindFragmentIn1(struct in_addr dst_addr,
1436                 struct in_addr alias_addr,
1437                 u_short ip_id)
1438 {
1439     struct alias_link *link;
1440 
1441     link = FindLinkIn(dst_addr, alias_addr,
1442                       NO_DEST_PORT, ip_id,
1443                       LINK_FRAGMENT_ID, 0);
1444 
1445     if (link == NULL)
1446     {
1447         link = AddLink(nullAddress, dst_addr, alias_addr,
1448                        NO_SRC_PORT, NO_DEST_PORT, ip_id,
1449                        LINK_FRAGMENT_ID);
1450     }
1451 
1452     return(link);
1453 }
1454 
1455 
1456 struct alias_link *
1457 FindFragmentIn2(struct in_addr dst_addr,   /* Doesn't add a link if one */
1458                 struct in_addr alias_addr, /*   is not found.           */
1459                 u_short ip_id)
1460 {
1461     return FindLinkIn(dst_addr, alias_addr,
1462                       NO_DEST_PORT, ip_id,
1463                       LINK_FRAGMENT_ID, 0);
1464 }
1465 
1466 
1467 struct alias_link *
1468 AddFragmentPtrLink(struct in_addr dst_addr,
1469                    u_short ip_id)
1470 {
1471     return AddLink(nullAddress, dst_addr, nullAddress,
1472                    NO_SRC_PORT, NO_DEST_PORT, ip_id,
1473                    LINK_FRAGMENT_PTR);
1474 }
1475 
1476 
1477 struct alias_link *
1478 FindFragmentPtr(struct in_addr dst_addr,
1479                 u_short ip_id)
1480 {
1481     return FindLinkIn(dst_addr, nullAddress,
1482                       NO_DEST_PORT, ip_id,
1483                       LINK_FRAGMENT_PTR, 0);
1484 }
1485 
1486 
1487 struct alias_link *
1488 FindProtoIn(struct in_addr dst_addr,
1489             struct in_addr alias_addr,
1490 	    u_char proto)
1491 {
1492     struct alias_link *link;
1493 
1494     link = FindLinkIn(dst_addr, alias_addr,
1495                       NO_DEST_PORT, 0,
1496                       proto, 1);
1497 
1498     if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1499     {
1500         struct in_addr target_addr;
1501 
1502         target_addr = FindOriginalAddress(alias_addr);
1503         link = AddLink(target_addr, dst_addr, alias_addr,
1504                        NO_SRC_PORT, NO_DEST_PORT, 0,
1505                        proto);
1506     }
1507 
1508     return (link);
1509 }
1510 
1511 
1512 struct alias_link *
1513 FindProtoOut(struct in_addr src_addr,
1514              struct in_addr dst_addr,
1515              u_char proto)
1516 {
1517     struct alias_link *link;
1518 
1519     link = FindLinkOut(src_addr, dst_addr,
1520                        NO_SRC_PORT, NO_DEST_PORT,
1521                        proto, 1);
1522 
1523     if (link == NULL)
1524     {
1525         struct in_addr alias_addr;
1526 
1527         alias_addr = FindAliasAddress(src_addr);
1528         link = AddLink(src_addr, dst_addr, alias_addr,
1529                        NO_SRC_PORT, NO_DEST_PORT, 0,
1530                        proto);
1531     }
1532 
1533     return (link);
1534 }
1535 
1536 
1537 struct alias_link *
1538 FindUdpTcpIn(struct in_addr dst_addr,
1539              struct in_addr alias_addr,
1540              u_short        dst_port,
1541              u_short        alias_port,
1542              u_char         proto)
1543 {
1544     int link_type;
1545     struct alias_link *link;
1546 
1547     switch (proto)
1548     {
1549     case IPPROTO_UDP:
1550         link_type = LINK_UDP;
1551         break;
1552     case IPPROTO_TCP:
1553         link_type = LINK_TCP;
1554         break;
1555     default:
1556         return NULL;
1557         break;
1558     }
1559 
1560     link = FindLinkIn(dst_addr, alias_addr,
1561                       dst_port, alias_port,
1562                       link_type, 1);
1563 
1564     if (!(packetAliasMode & PKT_ALIAS_DENY_INCOMING)
1565      && !(packetAliasMode & PKT_ALIAS_PROXY_ONLY)
1566      && link == NULL)
1567     {
1568         struct in_addr target_addr;
1569 
1570         target_addr = FindOriginalAddress(alias_addr);
1571         link = AddLink(target_addr, dst_addr, alias_addr,
1572                        alias_port, dst_port, alias_port,
1573                        link_type);
1574     }
1575 
1576     return(link);
1577 }
1578 
1579 
1580 struct alias_link *
1581 FindUdpTcpOut(struct in_addr  src_addr,
1582               struct in_addr  dst_addr,
1583               u_short         src_port,
1584               u_short         dst_port,
1585               u_char          proto)
1586 {
1587     int link_type;
1588     struct alias_link *link;
1589 
1590     switch (proto)
1591     {
1592     case IPPROTO_UDP:
1593         link_type = LINK_UDP;
1594         break;
1595     case IPPROTO_TCP:
1596         link_type = LINK_TCP;
1597         break;
1598     default:
1599         return NULL;
1600         break;
1601     }
1602 
1603     link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type, 1);
1604 
1605     if (link == NULL)
1606     {
1607         struct in_addr alias_addr;
1608 
1609         alias_addr = FindAliasAddress(src_addr);
1610         link = AddLink(src_addr, dst_addr, alias_addr,
1611                        src_port, dst_port, GET_ALIAS_PORT,
1612                        link_type);
1613     }
1614 
1615     return(link);
1616 }
1617 
1618 
1619 struct alias_link *
1620 FindPptpIn(struct in_addr dst_addr,
1621           struct in_addr alias_addr,
1622           u_short call_id)
1623 {
1624     struct alias_link *link;
1625 
1626     link = FindLinkIn(dst_addr, alias_addr,
1627                       NO_DEST_PORT, call_id,
1628                       LINK_PPTP, 1);
1629 
1630     if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1631     {
1632         struct in_addr target_addr;
1633 
1634         target_addr = FindOriginalAddress(alias_addr);
1635         link = AddLink(target_addr, dst_addr, alias_addr,
1636                        call_id, NO_DEST_PORT, call_id,
1637                        LINK_PPTP);
1638     }
1639 
1640     return(link);
1641 }
1642 
1643 
1644 struct alias_link *
1645 FindPptpOut(struct in_addr  src_addr,
1646             struct in_addr  dst_addr,
1647             u_short         call_id)
1648 {
1649     struct alias_link *link;
1650 
1651     link = FindLinkOut(src_addr, dst_addr,
1652                        call_id, NO_DEST_PORT,
1653                        LINK_PPTP, 1);
1654 
1655     if (link == NULL)
1656     {
1657         struct in_addr alias_addr;
1658 
1659         alias_addr = FindAliasAddress(src_addr);
1660         link = AddLink(src_addr, dst_addr, alias_addr,
1661                        call_id, NO_DEST_PORT, GET_ALIAS_PORT,
1662                        LINK_PPTP);
1663     }
1664 
1665     return(link);
1666 }
1667 
1668 
1669 struct alias_link *
1670 QueryUdpTcpIn(struct in_addr dst_addr,
1671               struct in_addr alias_addr,
1672               u_short        dst_port,
1673               u_short        alias_port,
1674               u_char         proto)
1675 {
1676     int link_type;
1677     struct alias_link *link;
1678 
1679     switch (proto)
1680     {
1681     case IPPROTO_UDP:
1682         link_type = LINK_UDP;
1683         break;
1684     case IPPROTO_TCP:
1685         link_type = LINK_TCP;
1686         break;
1687     default:
1688         return NULL;
1689         break;
1690     }
1691 
1692     link = FindLinkIn(dst_addr, alias_addr,
1693                       dst_port, alias_port,
1694                       link_type, 0);
1695 
1696     return(link);
1697 }
1698 
1699 
1700 struct alias_link *
1701 QueryUdpTcpOut(struct in_addr  src_addr,
1702                struct in_addr  dst_addr,
1703                u_short         src_port,
1704                u_short         dst_port,
1705                u_char          proto)
1706 {
1707     int link_type;
1708     struct alias_link *link;
1709 
1710     switch (proto)
1711     {
1712     case IPPROTO_UDP:
1713         link_type = LINK_UDP;
1714         break;
1715     case IPPROTO_TCP:
1716         link_type = LINK_TCP;
1717         break;
1718     default:
1719         return NULL;
1720         break;
1721     }
1722 
1723     link = FindLinkOut(src_addr, dst_addr,
1724                        src_port, dst_port,
1725 		       link_type, 0);
1726 
1727     return(link);
1728 }
1729 
1730 
1731 struct alias_link *
1732 FindRtspOut(struct in_addr  src_addr,
1733             struct in_addr  dst_addr,
1734             u_short         src_port,
1735             u_short         alias_port,
1736             u_char          proto)
1737 {
1738     int link_type;
1739     struct alias_link *link;
1740 
1741     switch (proto)
1742     {
1743     case IPPROTO_UDP:
1744         link_type = LINK_UDP;
1745         break;
1746     case IPPROTO_TCP:
1747         link_type = LINK_TCP;
1748         break;
1749     default:
1750         return NULL;
1751         break;
1752     }
1753 
1754     link = FindLinkOut(src_addr, dst_addr, src_port, 0, link_type, 1);
1755 
1756     if (link == NULL)
1757     {
1758         struct in_addr alias_addr;
1759 
1760         alias_addr = FindAliasAddress(src_addr);
1761         link = AddLink(src_addr, dst_addr, alias_addr,
1762                        src_port, 0, alias_port,
1763                        link_type);
1764     }
1765 
1766     return(link);
1767 }
1768 
1769 
1770 struct in_addr
1771 FindOriginalAddress(struct in_addr alias_addr)
1772 {
1773     struct alias_link *link;
1774 
1775     link = FindLinkIn(nullAddress, alias_addr,
1776                       0, 0, LINK_ADDR, 0);
1777     if (link == NULL)
1778     {
1779         newDefaultLink = 1;
1780         if (targetAddress.s_addr == INADDR_ANY)
1781             return alias_addr;
1782         else if (targetAddress.s_addr == INADDR_NONE)
1783             return aliasAddress;
1784         else
1785             return targetAddress;
1786     }
1787     else
1788     {
1789 	if (link->server != NULL) {		/* LSNAT link */
1790 	    struct in_addr src_addr;
1791 
1792 	    src_addr = link->server->addr;
1793 	    link->server = link->server->next;
1794 	    return (src_addr);
1795         } else if (link->src_addr.s_addr == INADDR_ANY)
1796             return aliasAddress;
1797         else
1798             return link->src_addr;
1799     }
1800 }
1801 
1802 
1803 struct in_addr
1804 FindAliasAddress(struct in_addr original_addr)
1805 {
1806     struct alias_link *link;
1807 
1808     link = FindLinkOut(original_addr, nullAddress,
1809                        0, 0, LINK_ADDR, 0);
1810     if (link == NULL)
1811     {
1812         return aliasAddress;
1813     }
1814     else
1815     {
1816         if (link->alias_addr.s_addr == INADDR_ANY)
1817             return aliasAddress;
1818         else
1819             return link->alias_addr;
1820     }
1821 }
1822 
1823 
1824 /* External routines for getting or changing link data
1825    (external to alias_db.c, but internal to alias*.c)
1826 
1827     SetFragmentData(), GetFragmentData()
1828     SetFragmentPtr(), GetFragmentPtr()
1829     SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
1830     GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
1831     GetOriginalPort(), GetAliasPort()
1832     SetAckModified(), GetAckModified()
1833     GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
1834     SetLastLineCrlfTermed(), GetLastLineCrlfTermed()
1835 */
1836 
1837 
1838 void
1839 SetFragmentAddr(struct alias_link *link, struct in_addr src_addr)
1840 {
1841     link->data.frag_addr = src_addr;
1842 }
1843 
1844 
1845 void
1846 GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr)
1847 {
1848     *src_addr = link->data.frag_addr;
1849 }
1850 
1851 
1852 void
1853 SetFragmentPtr(struct alias_link *link, char *fptr)
1854 {
1855     link->data.frag_ptr = fptr;
1856 }
1857 
1858 
1859 void
1860 GetFragmentPtr(struct alias_link *link, char **fptr)
1861 {
1862    *fptr = link->data.frag_ptr;
1863 }
1864 
1865 
1866 void
1867 SetStateIn(struct alias_link *link, int state)
1868 {
1869     /* TCP input state */
1870     switch (state) {
1871     case ALIAS_TCP_STATE_DISCONNECTED:
1872         if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED)
1873             link->expire_time = TCP_EXPIRE_DEAD;
1874         else
1875             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1876         break;
1877     case ALIAS_TCP_STATE_CONNECTED:
1878         if (link->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED)
1879             link->expire_time = TCP_EXPIRE_CONNECTED;
1880         break;
1881     default:
1882         abort();
1883     }
1884     link->data.tcp->state.in = state;
1885 }
1886 
1887 
1888 void
1889 SetStateOut(struct alias_link *link, int state)
1890 {
1891     /* TCP output state */
1892     switch (state) {
1893     case ALIAS_TCP_STATE_DISCONNECTED:
1894         if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED)
1895             link->expire_time = TCP_EXPIRE_DEAD;
1896         else
1897             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1898         break;
1899     case ALIAS_TCP_STATE_CONNECTED:
1900         if (link->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED)
1901             link->expire_time = TCP_EXPIRE_CONNECTED;
1902         break;
1903     default:
1904         abort();
1905     }
1906     link->data.tcp->state.out = state;
1907 }
1908 
1909 
1910 int
1911 GetStateIn(struct alias_link *link)
1912 {
1913     /* TCP input state */
1914     return link->data.tcp->state.in;
1915 }
1916 
1917 
1918 int
1919 GetStateOut(struct alias_link *link)
1920 {
1921     /* TCP output state */
1922     return link->data.tcp->state.out;
1923 }
1924 
1925 
1926 struct in_addr
1927 GetOriginalAddress(struct alias_link *link)
1928 {
1929     if (link->src_addr.s_addr == INADDR_ANY)
1930         return aliasAddress;
1931     else
1932         return(link->src_addr);
1933 }
1934 
1935 
1936 struct in_addr
1937 GetDestAddress(struct alias_link *link)
1938 {
1939     return(link->dst_addr);
1940 }
1941 
1942 
1943 struct in_addr
1944 GetAliasAddress(struct alias_link *link)
1945 {
1946     if (link->alias_addr.s_addr == INADDR_ANY)
1947         return aliasAddress;
1948     else
1949         return link->alias_addr;
1950 }
1951 
1952 
1953 struct in_addr
1954 GetDefaultAliasAddress()
1955 {
1956     return aliasAddress;
1957 }
1958 
1959 
1960 void
1961 SetDefaultAliasAddress(struct in_addr alias_addr)
1962 {
1963     aliasAddress = alias_addr;
1964 }
1965 
1966 
1967 u_short
1968 GetOriginalPort(struct alias_link *link)
1969 {
1970     return(link->src_port);
1971 }
1972 
1973 
1974 u_short
1975 GetAliasPort(struct alias_link *link)
1976 {
1977     return(link->alias_port);
1978 }
1979 
1980 #ifndef NO_FW_PUNCH
1981 static u_short
1982 GetDestPort(struct alias_link *link)
1983 {
1984     return(link->dst_port);
1985 }
1986 #endif
1987 
1988 void
1989 SetAckModified(struct alias_link *link)
1990 {
1991 /* Indicate that ACK numbers have been modified in a TCP connection */
1992     link->data.tcp->state.ack_modified = 1;
1993 }
1994 
1995 
1996 struct in_addr
1997 GetProxyAddress(struct alias_link *link)
1998 {
1999     return link->proxy_addr;
2000 }
2001 
2002 
2003 void
2004 SetProxyAddress(struct alias_link *link, struct in_addr addr)
2005 {
2006     link->proxy_addr = addr;
2007 }
2008 
2009 
2010 u_short
2011 GetProxyPort(struct alias_link *link)
2012 {
2013     return link->proxy_port;
2014 }
2015 
2016 
2017 void
2018 SetProxyPort(struct alias_link *link, u_short port)
2019 {
2020     link->proxy_port = port;
2021 }
2022 
2023 
2024 int
2025 GetAckModified(struct alias_link *link)
2026 {
2027 /* See if ACK numbers have been modified */
2028     return link->data.tcp->state.ack_modified;
2029 }
2030 
2031 
2032 int
2033 GetDeltaAckIn(struct ip *pip, struct alias_link *link)
2034 {
2035 /*
2036 Find out how much the ACK number has been altered for an incoming
2037 TCP packet.  To do this, a circular list of ACK numbers where the TCP
2038 packet size was altered is searched.
2039 */
2040 
2041     int i;
2042     struct tcphdr *tc;
2043     int delta, ack_diff_min;
2044     u_long ack;
2045 
2046     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2047     ack      = tc->th_ack;
2048 
2049     delta = 0;
2050     ack_diff_min = -1;
2051     for (i=0; i<N_LINK_TCP_DATA; i++)
2052     {
2053         struct ack_data_record x;
2054 
2055         x = link->data.tcp->ack[i];
2056         if (x.active == 1)
2057         {
2058             int ack_diff;
2059 
2060             ack_diff = SeqDiff(x.ack_new, ack);
2061             if (ack_diff >= 0)
2062             {
2063                 if (ack_diff_min >= 0)
2064                 {
2065                     if (ack_diff < ack_diff_min)
2066                     {
2067                         delta = x.delta;
2068                         ack_diff_min = ack_diff;
2069                     }
2070                 }
2071                 else
2072                 {
2073                     delta = x.delta;
2074                     ack_diff_min = ack_diff;
2075                 }
2076             }
2077         }
2078     }
2079     return (delta);
2080 }
2081 
2082 
2083 int
2084 GetDeltaSeqOut(struct ip *pip, struct alias_link *link)
2085 {
2086 /*
2087 Find out how much the sequence number has been altered for an outgoing
2088 TCP packet.  To do this, a circular list of ACK numbers where the TCP
2089 packet size was altered is searched.
2090 */
2091 
2092     int i;
2093     struct tcphdr *tc;
2094     int delta, seq_diff_min;
2095     u_long seq;
2096 
2097     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2098     seq = tc->th_seq;
2099 
2100     delta = 0;
2101     seq_diff_min = -1;
2102     for (i=0; i<N_LINK_TCP_DATA; i++)
2103     {
2104         struct ack_data_record x;
2105 
2106         x = link->data.tcp->ack[i];
2107         if (x.active == 1)
2108         {
2109             int seq_diff;
2110 
2111             seq_diff = SeqDiff(x.ack_old, seq);
2112             if (seq_diff >= 0)
2113             {
2114                 if (seq_diff_min >= 0)
2115                 {
2116                     if (seq_diff < seq_diff_min)
2117                     {
2118                         delta = x.delta;
2119                         seq_diff_min = seq_diff;
2120                     }
2121                 }
2122                 else
2123                 {
2124                     delta = x.delta;
2125                     seq_diff_min = seq_diff;
2126                 }
2127             }
2128         }
2129     }
2130     return (delta);
2131 }
2132 
2133 
2134 void
2135 AddSeq(struct ip *pip, struct alias_link *link, int delta)
2136 {
2137 /*
2138 When a TCP packet has been altered in length, save this
2139 information in a circular list.  If enough packets have
2140 been altered, then this list will begin to overwrite itself.
2141 */
2142 
2143     struct tcphdr *tc;
2144     struct ack_data_record x;
2145     int hlen, tlen, dlen;
2146     int i;
2147 
2148     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2149 
2150     hlen = (pip->ip_hl + tc->th_off) << 2;
2151     tlen = ntohs(pip->ip_len);
2152     dlen = tlen - hlen;
2153 
2154     x.ack_old = htonl(ntohl(tc->th_seq) + dlen);
2155     x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta);
2156     x.delta = delta;
2157     x.active = 1;
2158 
2159     i = link->data.tcp->state.index;
2160     link->data.tcp->ack[i] = x;
2161 
2162     i++;
2163     if (i == N_LINK_TCP_DATA)
2164         link->data.tcp->state.index = 0;
2165     else
2166         link->data.tcp->state.index = i;
2167 }
2168 
2169 void
2170 SetExpire(struct alias_link *link, int expire)
2171 {
2172     if (expire == 0)
2173     {
2174         link->flags &= ~LINK_PERMANENT;
2175         DeleteLink(link);
2176     }
2177     else if (expire == -1)
2178     {
2179         link->flags |= LINK_PERMANENT;
2180     }
2181     else if (expire > 0)
2182     {
2183         link->expire_time = expire;
2184     }
2185     else
2186     {
2187 #ifdef DEBUG
2188         fprintf(stderr, "PacketAlias/SetExpire(): ");
2189         fprintf(stderr, "error in expire parameter\n");
2190 #endif
2191     }
2192 }
2193 
2194 void
2195 ClearCheckNewLink(void)
2196 {
2197     newDefaultLink = 0;
2198 }
2199 
2200 void
2201 SetLastLineCrlfTermed(struct alias_link *link, int yes)
2202 {
2203 
2204     if (yes)
2205 	link->flags |= LINK_LAST_LINE_CRLF_TERMED;
2206     else
2207 	link->flags &= ~LINK_LAST_LINE_CRLF_TERMED;
2208 }
2209 
2210 int
2211 GetLastLineCrlfTermed(struct alias_link *link)
2212 {
2213 
2214     return (link->flags & LINK_LAST_LINE_CRLF_TERMED);
2215 }
2216 
2217 
2218 /* Miscellaneous Functions
2219 
2220     HouseKeeping()
2221     InitPacketAliasLog()
2222     UninitPacketAliasLog()
2223 */
2224 
2225 /*
2226     Whenever an outgoing or incoming packet is handled, HouseKeeping()
2227     is called to find and remove timed-out aliasing links.  Logic exists
2228     to sweep through the entire table and linked list structure
2229     every 60 seconds.
2230 
2231     (prototype in alias_local.h)
2232 */
2233 
2234 void
2235 HouseKeeping(void)
2236 {
2237     int i, n, n100;
2238     struct timeval tv;
2239     struct timezone tz;
2240 
2241     /*
2242      * Save system time (seconds) in global variable timeStamp for
2243      * use by other functions. This is done so as not to unnecessarily
2244      * waste timeline by making system calls.
2245      */
2246     gettimeofday(&tv, &tz);
2247     timeStamp = tv.tv_sec;
2248 
2249     /* Compute number of spokes (output table link chains) to cover */
2250     n100  = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual;
2251     n100 *= timeStamp - lastCleanupTime;
2252     n100 /= ALIAS_CLEANUP_INTERVAL_SECS;
2253 
2254     n = n100/100;
2255 
2256     /* Handle different cases */
2257     if (n > ALIAS_CLEANUP_MAX_SPOKES)
2258     {
2259         n = ALIAS_CLEANUP_MAX_SPOKES;
2260         lastCleanupTime = timeStamp;
2261         houseKeepingResidual = 0;
2262 
2263         for (i=0; i<n; i++)
2264             IncrementalCleanup();
2265     }
2266     else if (n > 0)
2267     {
2268         lastCleanupTime = timeStamp;
2269         houseKeepingResidual = n100 - 100*n;
2270 
2271         for (i=0; i<n; i++)
2272             IncrementalCleanup();
2273     }
2274     else if (n < 0)
2275     {
2276 #ifdef DEBUG
2277         fprintf(stderr, "PacketAlias/HouseKeeping(): ");
2278         fprintf(stderr, "something unexpected in time values\n");
2279 #endif
2280         lastCleanupTime = timeStamp;
2281         houseKeepingResidual = 0;
2282     }
2283 }
2284 
2285 
2286 /* Init the log file and enable logging */
2287 static void
2288 InitPacketAliasLog(void)
2289 {
2290    if ((~packetAliasMode & PKT_ALIAS_LOG)
2291     && (monitorFile = fopen("/var/log/alias.log", "w")))
2292    {
2293       packetAliasMode |= PKT_ALIAS_LOG;
2294       fprintf(monitorFile,
2295       "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
2296    }
2297 }
2298 
2299 
2300 /* Close the log-file and disable logging. */
2301 static void
2302 UninitPacketAliasLog(void)
2303 {
2304     if (monitorFile) {
2305         fclose(monitorFile);
2306         monitorFile = NULL;
2307     }
2308     packetAliasMode &= ~PKT_ALIAS_LOG;
2309 }
2310 
2311 
2312 
2313 
2314 
2315 
2316 /* Outside world interfaces
2317 
2318 -- "outside world" means other than alias*.c routines --
2319 
2320     PacketAliasRedirectPort()
2321     PacketAliasAddServer()
2322     PacketAliasRedirectProto()
2323     PacketAliasRedirectAddr()
2324     PacketAliasRedirectDelete()
2325     PacketAliasSetAddress()
2326     PacketAliasInit()
2327     PacketAliasUninit()
2328     PacketAliasSetMode()
2329 
2330 (prototypes in alias.h)
2331 */
2332 
2333 /* Redirection from a specific public addr:port to a
2334    private addr:port */
2335 struct alias_link *
2336 PacketAliasRedirectPort(struct in_addr src_addr,   u_short src_port,
2337                         struct in_addr dst_addr,   u_short dst_port,
2338                         struct in_addr alias_addr, u_short alias_port,
2339                         u_char proto)
2340 {
2341     int link_type;
2342     struct alias_link *link;
2343 
2344     switch(proto)
2345     {
2346     case IPPROTO_UDP:
2347         link_type = LINK_UDP;
2348         break;
2349     case IPPROTO_TCP:
2350         link_type = LINK_TCP;
2351         break;
2352     default:
2353 #ifdef DEBUG
2354         fprintf(stderr, "PacketAliasRedirectPort(): ");
2355         fprintf(stderr, "only TCP and UDP protocols allowed\n");
2356 #endif
2357         return NULL;
2358     }
2359 
2360     link = AddLink(src_addr, dst_addr, alias_addr,
2361                    src_port, dst_port, alias_port,
2362                    link_type);
2363 
2364     if (link != NULL)
2365     {
2366         link->flags |= LINK_PERMANENT;
2367     }
2368 #ifdef DEBUG
2369     else
2370     {
2371         fprintf(stderr, "PacketAliasRedirectPort(): "
2372                         "call to AddLink() failed\n");
2373     }
2374 #endif
2375 
2376     return link;
2377 }
2378 
2379 /* Add server to the pool of servers */
2380 int
2381 PacketAliasAddServer(struct alias_link *link, struct in_addr addr, u_short port)
2382 {
2383     struct server *server;
2384 
2385     server = malloc(sizeof(struct server));
2386 
2387     if (server != NULL) {
2388 	struct server *head;
2389 
2390 	server->addr = addr;
2391 	server->port = port;
2392 
2393 	head = link->server;
2394 	if (head == NULL)
2395 	    server->next = server;
2396 	else {
2397 	    struct server *s;
2398 
2399 	    for (s = head; s->next != head; s = s->next);
2400 	    s->next = server;
2401 	    server->next = head;
2402 	}
2403 	link->server = server;
2404 	return (0);
2405     } else
2406 	return (-1);
2407 }
2408 
2409 /* Redirect packets of a given IP protocol from a specific
2410    public address to a private address */
2411 struct alias_link *
2412 PacketAliasRedirectProto(struct in_addr src_addr,
2413                          struct in_addr dst_addr,
2414                          struct in_addr alias_addr,
2415                          u_char proto)
2416 {
2417     struct alias_link *link;
2418 
2419     link = AddLink(src_addr, dst_addr, alias_addr,
2420                    NO_SRC_PORT, NO_DEST_PORT, 0,
2421                    proto);
2422 
2423     if (link != NULL)
2424     {
2425         link->flags |= LINK_PERMANENT;
2426     }
2427 #ifdef DEBUG
2428     else
2429     {
2430         fprintf(stderr, "PacketAliasRedirectProto(): "
2431                         "call to AddLink() failed\n");
2432     }
2433 #endif
2434 
2435     return link;
2436 }
2437 
2438 /* Static address translation */
2439 struct alias_link *
2440 PacketAliasRedirectAddr(struct in_addr src_addr,
2441                         struct in_addr alias_addr)
2442 {
2443     struct alias_link *link;
2444 
2445     link = AddLink(src_addr, nullAddress, alias_addr,
2446                    0, 0, 0,
2447                    LINK_ADDR);
2448 
2449     if (link != NULL)
2450     {
2451         link->flags |= LINK_PERMANENT;
2452     }
2453 #ifdef DEBUG
2454     else
2455     {
2456         fprintf(stderr, "PacketAliasRedirectAddr(): "
2457                         "call to AddLink() failed\n");
2458     }
2459 #endif
2460 
2461     return link;
2462 }
2463 
2464 
2465 void
2466 PacketAliasRedirectDelete(struct alias_link *link)
2467 {
2468 /* This is a dangerous function to put in the API,
2469    because an invalid pointer can crash the program. */
2470 
2471     deleteAllLinks = 1;
2472     DeleteLink(link);
2473     deleteAllLinks = 0;
2474 }
2475 
2476 
2477 void
2478 PacketAliasSetAddress(struct in_addr addr)
2479 {
2480     if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
2481      && aliasAddress.s_addr != addr.s_addr)
2482         CleanupAliasData();
2483 
2484     aliasAddress = addr;
2485 }
2486 
2487 
2488 void
2489 PacketAliasSetTarget(struct in_addr target_addr)
2490 {
2491     targetAddress = target_addr;
2492 }
2493 
2494 
2495 void
2496 PacketAliasInit(void)
2497 {
2498     int i;
2499     struct timeval tv;
2500     struct timezone tz;
2501     static int firstCall = 1;
2502 
2503     if (firstCall == 1)
2504     {
2505         gettimeofday(&tv, &tz);
2506         timeStamp = tv.tv_sec;
2507         lastCleanupTime = tv.tv_sec;
2508         houseKeepingResidual = 0;
2509 
2510         for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
2511             LIST_INIT(&linkTableOut[i]);
2512         for (i=0; i<LINK_TABLE_IN_SIZE; i++)
2513             LIST_INIT(&linkTableIn[i]);
2514 
2515         atexit(PacketAliasUninit);
2516         firstCall = 0;
2517     }
2518     else
2519     {
2520         deleteAllLinks = 1;
2521         CleanupAliasData();
2522         deleteAllLinks = 0;
2523     }
2524 
2525     aliasAddress.s_addr = INADDR_ANY;
2526     targetAddress.s_addr = INADDR_ANY;
2527 
2528     icmpLinkCount = 0;
2529     udpLinkCount = 0;
2530     tcpLinkCount = 0;
2531     pptpLinkCount = 0;
2532     protoLinkCount = 0;
2533     fragmentIdLinkCount = 0;
2534     fragmentPtrLinkCount = 0;
2535     sockCount = 0;
2536 
2537     cleanupIndex =0;
2538 
2539     packetAliasMode = PKT_ALIAS_SAME_PORTS
2540                     | PKT_ALIAS_USE_SOCKETS
2541                     | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
2542 }
2543 
2544 void
2545 PacketAliasUninit(void) {
2546     deleteAllLinks = 1;
2547     CleanupAliasData();
2548     deleteAllLinks = 0;
2549     UninitPacketAliasLog();
2550 #ifndef NO_FW_PUNCH
2551     UninitPunchFW();
2552 #endif
2553 }
2554 
2555 
2556 /* Change mode for some operations */
2557 unsigned int
2558 PacketAliasSetMode(
2559     unsigned int flags, /* Which state to bring flags to */
2560     unsigned int mask   /* Mask of which flags to affect (use 0 to do a
2561                            probe for flag values) */
2562 )
2563 {
2564 /* Enable logging? */
2565     if (flags & mask & PKT_ALIAS_LOG)
2566     {
2567         InitPacketAliasLog();     /* Do the enable */
2568     } else
2569 /* _Disable_ logging? */
2570     if (~flags & mask & PKT_ALIAS_LOG) {
2571         UninitPacketAliasLog();
2572     }
2573 
2574 #ifndef NO_FW_PUNCH
2575 /* Start punching holes in the firewall? */
2576     if (flags & mask & PKT_ALIAS_PUNCH_FW) {
2577         InitPunchFW();
2578     } else
2579 /* Stop punching holes in the firewall? */
2580     if (~flags & mask & PKT_ALIAS_PUNCH_FW) {
2581         UninitPunchFW();
2582     }
2583 #endif
2584 
2585 /* Other flags can be set/cleared without special action */
2586     packetAliasMode = (flags & mask) | (packetAliasMode & ~mask);
2587     return packetAliasMode;
2588 }
2589 
2590 
2591 int
2592 PacketAliasCheckNewLink(void)
2593 {
2594     return newDefaultLink;
2595 }
2596 
2597 
2598 #ifndef NO_FW_PUNCH
2599 
2600 /*****************
2601   Code to support firewall punching.  This shouldn't really be in this
2602   file, but making variables global is evil too.
2603   ****************/
2604 
2605 /* Firewall include files */
2606 #include <net/if.h>
2607 #include <netinet/ip_fw.h>
2608 #include <string.h>
2609 #include <err.h>
2610 
2611 static void ClearAllFWHoles(void);
2612 
2613 static int fireWallBaseNum;     /* The first firewall entry free for our use */
2614 static int fireWallNumNums;     /* How many entries can we use? */
2615 static int fireWallActiveNum;   /* Which entry did we last use? */
2616 static char *fireWallField;     /* bool array for entries */
2617 
2618 #define fw_setfield(field, num)                         \
2619 do {                                                    \
2620     (field)[(num) - fireWallBaseNum] = 1;               \
2621 } /*lint -save -e717 */ while(0) /*lint -restore */
2622 #define fw_clrfield(field, num)                         \
2623 do {                                                    \
2624     (field)[(num) - fireWallBaseNum] = 0;               \
2625 } /*lint -save -e717 */ while(0) /*lint -restore */
2626 #define fw_tstfield(field, num) ((field)[(num) - fireWallBaseNum])
2627 
2628 void
2629 PacketAliasSetFWBase(unsigned int base, unsigned int num) {
2630     fireWallBaseNum = base;
2631     fireWallNumNums = num;
2632 }
2633 
2634 static void
2635 InitPunchFW(void) {
2636     fireWallField = malloc(fireWallNumNums);
2637     if (fireWallField) {
2638         memset(fireWallField, 0, fireWallNumNums);
2639         if (fireWallFD < 0) {
2640             fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2641         }
2642         ClearAllFWHoles();
2643         fireWallActiveNum = fireWallBaseNum;
2644     }
2645 }
2646 
2647 static void
2648 UninitPunchFW(void) {
2649     ClearAllFWHoles();
2650     if (fireWallFD >= 0)
2651         close(fireWallFD);
2652     fireWallFD = -1;
2653     if (fireWallField)
2654         free(fireWallField);
2655     fireWallField = NULL;
2656     packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
2657 }
2658 
2659 /* Make a certain link go through the firewall */
2660 void
2661 PunchFWHole(struct alias_link *link) {
2662     int r;                      /* Result code */
2663     struct ip_fw rule;          /* On-the-fly built rule */
2664     int fwhole;                 /* Where to punch hole */
2665 
2666 /* Don't do anything unless we are asked to */
2667     if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
2668          fireWallFD < 0 ||
2669          link->link_type != LINK_TCP ||
2670          !link->data.tcp)
2671         return;
2672 
2673     memset(&rule, 0, sizeof rule);
2674 
2675 /** Build rule **/
2676 
2677     /* Find empty slot */
2678     for (fwhole = fireWallActiveNum;
2679          fwhole < fireWallBaseNum + fireWallNumNums &&
2680              fw_tstfield(fireWallField, fwhole);
2681          fwhole++)
2682         ;
2683     if (fwhole == fireWallBaseNum + fireWallNumNums) {
2684         for (fwhole = fireWallBaseNum;
2685              fwhole < fireWallActiveNum &&
2686                  fw_tstfield(fireWallField, fwhole);
2687              fwhole++)
2688             ;
2689         if (fwhole == fireWallActiveNum) {
2690             /* No rule point empty - we can't punch more holes. */
2691             fireWallActiveNum = fireWallBaseNum;
2692 #ifdef DEBUG
2693             fprintf(stderr, "libalias: Unable to create firewall hole!\n");
2694 #endif
2695             return;
2696         }
2697     }
2698     /* Start next search at next position */
2699     fireWallActiveNum = fwhole+1;
2700 
2701     /* Build generic part of the two rules */
2702     rule.fw_number = fwhole;
2703     IP_FW_SETNSRCP(&rule, 1);	/* Number of source ports. */
2704     IP_FW_SETNDSTP(&rule, 1);	/* Number of destination ports. */
2705     rule.fw_flg = IP_FW_F_ACCEPT | IP_FW_F_IN | IP_FW_F_OUT;
2706     rule.fw_prot = IPPROTO_TCP;
2707     rule.fw_smsk.s_addr = INADDR_BROADCAST;
2708     rule.fw_dmsk.s_addr = INADDR_BROADCAST;
2709 
2710     /* Build and apply specific part of the rules */
2711     rule.fw_src = GetOriginalAddress(link);
2712     rule.fw_dst = GetDestAddress(link);
2713     rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link));
2714     rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link));
2715 
2716     /* Skip non-bound links - XXX should not be strictly necessary,
2717        but seems to leave hole if not done.  Leak of non-bound links?
2718        (Code should be left even if the problem is fixed - it is a
2719        clear optimization) */
2720     if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) {
2721         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2722 #ifdef DEBUG
2723         if (r)
2724             err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2725 #endif
2726         rule.fw_src = GetDestAddress(link);
2727         rule.fw_dst = GetOriginalAddress(link);
2728         rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link));
2729         rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link));
2730         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2731 #ifdef DEBUG
2732         if (r)
2733             err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2734 #endif
2735     }
2736 /* Indicate hole applied */
2737     link->data.tcp->fwhole = fwhole;
2738     fw_setfield(fireWallField, fwhole);
2739 }
2740 
2741 /* Remove a hole in a firewall associated with a particular alias
2742    link.  Calling this too often is harmless. */
2743 static void
2744 ClearFWHole(struct alias_link *link) {
2745     if (link->link_type == LINK_TCP && link->data.tcp) {
2746         int fwhole =  link->data.tcp->fwhole; /* Where is the firewall hole? */
2747         struct ip_fw rule;
2748 
2749         if (fwhole < 0)
2750             return;
2751 
2752         memset(&rule, 0, sizeof rule);
2753         rule.fw_number = fwhole;
2754         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2755             ;
2756         fw_clrfield(fireWallField, fwhole);
2757         link->data.tcp->fwhole = -1;
2758     }
2759 }
2760 
2761 /* Clear out the entire range dedicated to firewall holes. */
2762 static void
2763 ClearAllFWHoles(void) {
2764     struct ip_fw rule;          /* On-the-fly built rule */
2765     int i;
2766 
2767     if (fireWallFD < 0)
2768         return;
2769 
2770     memset(&rule, 0, sizeof rule);
2771     for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) {
2772         rule.fw_number = i;
2773         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2774             ;
2775     }
2776     memset(fireWallField, 0, fireWallNumNums);
2777 }
2778 #endif
2779