1/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
2/*
3 * tftpd_mcast.c
4 *    support routine for multicast server
5 *
6 * $Id: tftpd_mcast.c,v 1.6 2003/04/25 00:16:19 jp Exp $
7 *
8 * Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
9 *                and Remi Lefebvre <remi@debian.org>
10 *
11 * atftp is free software; you can redistribute them and/or modify them
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
15 *
16 */
17
18#include "config.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <sys/types.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include "tftpd.h"
27#include "tftp_def.h"
28#include "logger.h"
29
30#define START    0
31#define GET_ENUM 1
32#define GET_GRP  2
33#define EXPAND   3
34
35pthread_mutex_t mcast_tid_list = PTHREAD_MUTEX_INITIALIZER;
36
37int parse_ip(char *string, char **res_ip);
38int parse_port(char *string, char **res_port);
39
40struct tid {
41     char *addr;
42     short port;
43     int used;
44     struct tid *next;
45};
46
47struct tid *tid_list = NULL;
48
49/*
50 * Return a free IP/Port for the multicast transfer
51 */
52int tftpd_mcast_get_tid(char **addr, short *port)
53{
54     struct tid *current = tid_list;
55
56     pthread_mutex_lock(&mcast_tid_list);
57     /* walk the list for a free tid */
58     while (current != NULL)
59     {
60	  if (current->used == 0)
61	  {
62	       *addr = current->addr;
63	       *port = current->port;
64	       current->used = 1;
65	       pthread_mutex_unlock(&mcast_tid_list);
66	       return OK;
67	  }
68	  else
69	       current = current->next;
70     }
71     pthread_mutex_unlock(&mcast_tid_list);
72     return ERR;
73}
74
75int tftpd_mcast_free_tid(char *addr, short port)
76{
77     struct tid *current = tid_list;
78
79     pthread_mutex_lock(&mcast_tid_list);
80     while (current != NULL)
81     {
82	  if ((current->used == 1) && (current->port == port) &&
83	      (strcmp(current->addr, addr) == 0))
84	  {
85	       current->used = 0;
86	       pthread_mutex_unlock(&mcast_tid_list);
87	       return OK;
88	  }
89	  else
90	       current = current->next;
91     }
92     pthread_mutex_unlock(&mcast_tid_list);
93     return ERR;
94}
95
96/* valid address specification:
97   239.255.0.0-239.255.0.20
98   239.255.0.0-20
99   239.255.0.0,1,2,3,8,10
100*/
101/* valid port specification
102   1758
103   1758-1780
104   1758,1760,4000
105*/
106int tftpd_mcast_parse_opt(char *addr, char *ports)
107{
108     char *ip;
109     char *port;
110     struct tid *current = NULL;
111     struct tid *tmp = NULL;
112
113     while (1)
114     {
115	  if (parse_ip(addr, &ip) !=  OK)
116	       return ERR;
117	  if (ip == NULL)
118	       return OK;
119	  while (1)
120	  {
121	       if (parse_port(ports, &port) != OK)
122		    return ERR;
123	       if (port == NULL)
124		    break;
125	       /* add this ip/port to the tid list */
126	       tmp = malloc(sizeof(struct tid));
127	       tmp->addr = strdup(ip);
128	       tmp->port = (short)atoi(port);
129	       tmp->used = 0;
130	       tmp->next = NULL;
131	       if (tid_list == NULL)
132	       {
133		    tid_list = tmp;
134		    current = tid_list;
135	       }
136	       else
137	       {
138		    current->next = tmp;
139		    current = tmp;
140	       }
141	  }
142     }
143}
144
145void tftpd_mcast_clean(void)
146{
147
148
149}
150
151int parse_ip(char *string, char **res_ip)
152{
153     static int state = START;
154
155     static char s[MAXLEN];
156     static char *saveptr;
157     static char s2[MAXLEN];
158     static char *saveptr2;
159
160     static int ip[4];
161     static int tmp_ip[4];
162
163     static int i;
164
165     int res;
166     char *tmp = NULL, *tmp2 = NULL;
167     static char out[MAXLEN];
168
169     *res_ip = NULL;
170
171     while (1)
172     {
173	  switch (state)
174	  {
175	  case START:
176	       Strncpy(s, string, MAXLEN);
177	       tmp = strtok_r(s, ",", &saveptr);
178	       if (tmp == NULL)
179	       {
180		    state = START;
181		    return ERR;
182	       }
183	       else
184		    state = GET_GRP;
185			 break;
186	  case GET_ENUM:
187	       tmp = strtok_r(NULL, ",", &saveptr);
188	       if (tmp == NULL)
189	       {
190		    state = START;
191		    return OK;
192	       }
193	       else
194		    state = GET_GRP;
195			 break;
196	  case GET_GRP:
197	       Strncpy(s2, tmp, MAXLEN);
198	       tmp = strtok_r(s2, "-", &saveptr2);
199	       if (tmp == NULL)
200	       {
201		    state = START;
202		    return ERR;
203	       }
204	       res = sscanf(tmp, "%d.%d.%d.%d", &tmp_ip[0], &tmp_ip[1],
205			    &tmp_ip[2], &tmp_ip[3]);
206	       if (res == 4)
207	       {
208		    for (i=0; i < 4; i++)
209			 ip[i] = tmp_ip[i];
210	       }
211	       else
212	       {
213		    if (res == 1)
214		    {
215			 ip[3] = tmp_ip[0];
216		    }
217		    else
218		    {
219			 state = START;
220			 return ERR;
221		    }
222	       }
223	       tmp2 = strtok_r(NULL, "-", &saveptr2);
224	       if (tmp2 == NULL)
225	       {
226		    state = GET_ENUM;
227		    snprintf(out, sizeof(out), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
228		    *res_ip = out;
229		    return OK;
230	       }
231	       else
232	       {
233		    sscanf(tmp2, "%d", &tmp_ip[0]);
234		    i = ip[3];
235		    if (i >= tmp_ip[0])
236		    {
237			 logger(LOG_ERR, "Bad address range: %d.%d.%d.%d-%d",
238				ip[0], ip[1], ip[2], ip[3], tmp_ip[0]);
239			 return ERR;
240		    }
241		    state = EXPAND;
242	       }
243	       break;
244	  case EXPAND:
245	       if (i > tmp_ip[0])
246	       {
247		    state = GET_ENUM;
248		    break;
249	       }
250	       snprintf(out, sizeof(out), "%d.%d.%d.%d", ip[0], ip[1], ip[2], i);
251	       i++;
252	       *res_ip = out;
253	       return OK;
254	       break;
255	  }
256     }
257}
258
259int parse_port(char *string, char **res_port)
260{
261     static int state = START;
262
263     static char s[MAXLEN];
264     static char *saveptr;
265     static char s2[MAXLEN];
266     static char *saveptr2;
267
268     static int port;
269     static int tmp_port;
270
271     static int i;
272
273     int res;
274     char *tmp = NULL, *tmp2 = NULL;
275     static char out[MAXLEN];
276
277     *res_port = NULL;
278
279     while (1)
280     {
281	  switch (state)
282	  {
283	  case START:
284	       Strncpy(s, string, MAXLEN);
285	       tmp = strtok_r(s, ",", &saveptr);
286	       if (tmp == NULL)
287	       {
288		    state = START;
289		    return ERR;
290	       }
291	       else
292		    state = GET_GRP;
293			 break;
294	  case GET_ENUM:
295	       tmp = strtok_r(NULL, ",", &saveptr);
296	       if (tmp == NULL)
297	       {
298		    state = START;
299		    return OK;
300	       }
301	       else
302		    state = GET_GRP;
303			 break;
304	  case GET_GRP:
305	       Strncpy(s2, tmp, MAXLEN);
306	       tmp = strtok_r(s2, "-", &saveptr2);
307	       if (tmp == NULL)
308	       {
309		    state = START;
310		    return ERR;
311	       }
312	       res = sscanf(tmp, "%d", &port);
313	       if (res != 1)
314	       {
315		    state = START;
316		    return ERR;
317	       }
318	       tmp2 = strtok_r(NULL, "-", &saveptr2);
319	       if (tmp2 == NULL)
320	       {
321		    state = GET_ENUM;
322		    snprintf(out, sizeof(out), "%d", port);
323		    *res_port = out;
324		    return OK;
325	       }
326	       else
327	       {
328		    sscanf(tmp2, "%d", &tmp_port);
329		    i = port;
330		    if (i >= tmp_port)
331		    {
332			 logger(LOG_ERR, "Bad port range: %d-%d", i, tmp_port);
333			 return ERR;
334		    }
335		    state = EXPAND;
336	       }
337	       break;
338	  case EXPAND:
339	       if (i > tmp_port)
340	       {
341		    state = GET_ENUM;
342		    break;
343	       }
344	       snprintf(out, sizeof(out), "%d", i);
345	       i++;
346	       *res_port = out;
347	       return OK;
348	       break;
349	  }
350     }
351}
352