1 /* Copyright 2003-2008 Wang, Chun-Pin All rights reserved. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/socket.h>
5 #include <string.h>
6 #include <syslog.h>
7 
8 #include "smbftpd.h"
9 
10 typedef struct {
11 	union sockunion addr;
12 	pid_t pid;
13 } smbftpd_iptable_t;
14 
15 static int table_size = 0;
16 static smbftpd_iptable_t *iptable;
17 
18 /**
19  * Free the ip table.
20  */
smbftpd_iptrack_free()21 void smbftpd_iptrack_free()
22 {
23 	if (iptable) {
24 		free(iptable);
25 		iptable = NULL;
26 	}
27 	table_size = 0;
28 }
29 
30 /**
31  * Allocate table to record ip address. So we can use
32  * smbftpd_iptrack_add(), smbftpd_iptrack_check(),
33  * smbftpd_iptrack_delete() to control max connection from
34  * the same IP.
35  *
36  * @param maxclient Max client to accept
37  *
38  * @return 0: Success
39  *         -1: Failed
40  */
smbftpd_iptrack_alloc(int maxclient)41 int smbftpd_iptrack_alloc(int maxclient)
42 {
43 	if (iptable) {
44 		smbftpd_iptrack_free();
45 	}
46 
47 	if (!maxclient) {
48 		return 0;
49 	}
50 
51 	iptable = calloc(maxclient, sizeof(smbftpd_iptable_t));
52 	if (!iptable) {
53 		return -1;
54 	}
55 	table_size = maxclient;
56 
57 	return 0;
58 }
59 
60 /**
61  * Add an IP/pid into iptable
62  *
63  * @param addr   The client's ip address
64  * @param pid    The process id of the fork()ed process
65  */
smbftpd_iptrack_add(union sockunion * addr,pid_t pid)66 void smbftpd_iptrack_add(union sockunion *addr, pid_t pid)
67 {
68 	int i = 0;
69 
70 	if (!iptable) {
71 		return;
72 	}
73 
74 	do {
75 		if (iptable[i].pid == 0) {
76 			iptable[i].addr = *addr;
77 			iptable[i].pid = pid;
78 			return;
79 		}
80 		i++;
81 	} while ( i < table_size );
82 
83 	// Table full, Remove the first item. Should never happen?
84 	memmove(&(iptable[0]), &(iptable[1]), sizeof(iptable[0]) * (table_size - 1));
85 	iptable[table_size-1].addr = *addr;
86 	iptable[table_size-1].pid = pid;
87 
88 	return;
89 }
90 
91 /**
92  * Check whether the max connection from the same ip
93  * has reached limit
94  *
95  * @param maxip  Max connection per ip.
96  * @param addr   The client's IP
97  *
98  * @return 0: Allowed
99  *         -1: Exceed the limit
100  */
smbftpd_iptrack_check(int maxip,union sockunion * addr)101 int smbftpd_iptrack_check(int maxip, union sockunion *addr)
102 {
103 	int i = 0;
104 	int match = 0;
105 
106 	if (!iptable || !maxip || !addr) {
107 		return 0;
108 	}
109 
110 	do {
111 		if (iptable[i].pid != 0 && iptable[i].addr.su_family == addr->su_family) {
112 			if (iptable[i].addr.su_family == AF_INET &&
113 				iptable[i].addr.su_sin.sin_addr.s_addr == addr->su_sin.sin_addr.s_addr) {
114 				match++;
115 #ifdef INET6
116 			} else if (iptable[i].addr.su_family == AF_INET6 &&
117 					   IN6_ARE_ADDR_EQUAL(&iptable[i].addr.su_sin6.sin6_addr, &addr->su_sin6.sin6_addr)) {
118 				match++;
119 #endif
120 			}
121 		}
122 		i++;
123 	} while ( i < table_size );
124 
125 	if (match >= maxip) {
126 		return -1;
127 	}
128 
129 	return 0;
130 }
131 
132 /**
133  * Remove the pid/ip from iptable.
134  *
135  * This function is called when client disconnects
136  *
137  * @param pid    The process id of fork()ed process
138  */
smbftpd_iptrack_delete(pid_t pid)139 void smbftpd_iptrack_delete(pid_t pid)
140 {
141 	int i = 0;
142 
143 	if (!iptable) {
144 		return;
145 	}
146 
147 	do {
148 		if (iptable[i].pid == pid) {
149 			iptable[i].pid = 0;
150 			return;
151 		}
152 		i++;
153 	} while ( i < table_size );
154 }
155 
156