Contiki 3.x
ftpc.c
1 /*
2  * Copyright (c) 2004, Adam Dunkels.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  * Author: Adam Dunkels <adam@sics.se>
32  *
33  */
34 #include "contiki.h"
35 #include "ftpc.h"
36 #include "lib/petsciiconv.h"
37 
38 #include <string.h>
39 #include <stdio.h>
40 
41 #define ISO_nl 0x0a
42 #define ISO_cr 0x0d
43 
44 #define DATAPORT 6510
45 
46 #define MAX_FILENAMELEN 32
47 
48 struct ftp_dataconn {
49  unsigned char type;
50  unsigned char conntype;
51 #define CONNTYPE_LIST 0
52 #define CONNTYPE_FILE 1
53 
54  uint16_t port;
55 
56  unsigned char filenameptr;
57  char filename[MAX_FILENAMELEN];
58 
59 
60 };
61 
62 struct ftp_connection {
63  unsigned char type;
64 #define TYPE_CONTROL 1
65 #define TYPE_DATA 2
66 #define TYPE_ABORT 3
67 #define TYPE_CLOSE 4
68 
69  unsigned char state;
70 #define STATE_NONE 0
71 #define STATE_INITIAL 1
72 #define STATE_SEND_USER 2
73 #define STATE_USER_SENT 3
74 #define STATE_SEND_PASS 4
75 #define STATE_PASS_SENT 5
76 #define STATE_SEND_PORT 6
77 #define STATE_PORT_SENT 7
78 #define STATE_SEND_OPTIONS 8
79 #define STATE_OPTION_SENT 9
80 #define STATE_CONNECTED 10
81 #define STATE_SEND_NLST 11
82 #define STATE_NLST_SENT 12
83 #define STATE_SEND_RETR 13
84 #define STATE_RETR_SENT 14
85 
86 #define STATE_SEND_CWD 15
87 #define STATE_CWD_SENT 16
88 
89 #define STATE_SEND_CDUP 17
90 #define STATE_CDUP_SENT 18
91 
92 #define STATE_SEND_QUIT 19
93 #define STATE_QUIT_SENT 20
94 
95  unsigned char connected_confirmed;
96 
97  struct ftp_dataconn dataconn;
98 
99  char code[3];
100  unsigned char codeptr;
101 
102  unsigned char optionsptr;
103 
104  char filename[MAX_FILENAMELEN];
105 
106 };
107 
108 #define NUM_OPTIONS 1
109 static const struct {
110  unsigned char num;
111  char *commands[NUM_OPTIONS];
112 } options = {
113  NUM_OPTIONS,
114  {"TYPE I\r\n"}
115 };
116 
117 static struct ftp_connection *waiting_for_dataconn;
118 
119 MEMB(connections, struct ftp_connection, 1);
120 
121 /*---------------------------------------------------------------------------*/
122 void
123 ftpc_init(void)
124 {
125  memb_init(&connections);
126  /* tcp_listen(UIP_HTONS(DATAPORT));*/
127 }
128 /*---------------------------------------------------------------------------*/
129 void *
130 ftpc_connect(uip_ipaddr_t *ipaddr, uint16_t port)
131 {
132  struct ftp_connection *c;
133 
134  c = (struct ftp_connection *)memb_alloc(&connections);
135  if(c == NULL) {
136  return NULL;
137  }
138  c->type = TYPE_CONTROL;
139  c->state = STATE_INITIAL;
140  c->connected_confirmed = 0;
141  c->codeptr = 0;
142  c->dataconn.type = TYPE_DATA;
143  c->dataconn.port = DATAPORT;
144  tcp_listen(UIP_HTONS(DATAPORT));
145 
146  if(tcp_connect(ipaddr, port, c) == NULL) {
147  memb_free(&connections, c);
148  return NULL;
149  }
150 
151  return c;
152 }
153 /*---------------------------------------------------------------------------*/
154 static void
155 handle_input(struct ftp_connection *c)
156 {
157  int code;
158 
159  code = (c->code[0] - '0') * 100 +
160  (c->code[1] - '0') * 10 +
161  (c->code[2] - '0');
162  /* printf("Handle input code %d state %d\n", code, c->state);*/
163 
164  if(c->state == STATE_INITIAL) {
165  if(code == 220) {
166  c->state = STATE_SEND_USER;
167  }
168  } else if(c->state == STATE_USER_SENT) {
169  if(code == 331) {
170  c->state = STATE_SEND_PASS;
171  }
172  } else if(c->state == STATE_PASS_SENT) {
173  if(code == 230) {
174  c->state = STATE_SEND_OPTIONS;
175  c->optionsptr = 0;
176  }
177  } else if(c->state == STATE_PORT_SENT) {
178  c->state = STATE_CONNECTED;
179  if(c->connected_confirmed == 0) {
180  ftpc_connected(c);
181  c->connected_confirmed = 1;
182  }
183  } else if(c->state == STATE_OPTION_SENT) {
184  if(c->optionsptr >= options.num) {
185  c->state = STATE_SEND_PORT;
186  } else {
187  c->state = STATE_SEND_OPTIONS;
188  }
189  } else if((c->state == STATE_NLST_SENT ||
190  c->state == STATE_RETR_SENT ||
191  c->state == STATE_CONNECTED)) {
192  if(code == 226 || code == 550) {
193  tcp_unlisten(uip_htons(c->dataconn.port));
194  ++c->dataconn.port;
195  tcp_listen(uip_htons(c->dataconn.port));
196  c->state = STATE_SEND_PORT;
197  }
198 
199  if(code == 550) {
200  ftpc_list_file(NULL);
201  }
202  } else if(c->state == STATE_CWD_SENT ||
203  c->state == STATE_CDUP_SENT) {
204  c->state = STATE_CONNECTED;
205  ftpc_cwd_done(code);
206  /* } else if(c->state == STATE_) {
207  c->state = STATE_CONNECTED;*/
208  }
209 }
210 /*---------------------------------------------------------------------------*/
211 static void
212 newdata(struct ftp_connection *c)
213 {
214  uint16_t i;
215  uint8_t d;
216 
217  for(i = 0; i < uip_datalen(); ++i) {
218  d = ((char *)uip_appdata)[i];
219  if(c->codeptr < sizeof(c->code)) {
220  c->code[c->codeptr] = d;
221  ++c->codeptr;
222  }
223 
224  if(d == ISO_nl) {
225  handle_input(c);
226  c->codeptr = 0;
227  }
228  }
229 }
230 /*---------------------------------------------------------------------------*/
231 static void
232 acked(struct ftp_connection *c)
233 {
234  switch(c->state) {
235  case STATE_SEND_USER:
236  c->state = STATE_USER_SENT;
237  break;
238  case STATE_SEND_PASS:
239  c->state = STATE_PASS_SENT;
240  break;
241  case STATE_SEND_PORT:
242  c->state = STATE_PORT_SENT;
243  break;
244  case STATE_SEND_OPTIONS:
245  ++c->optionsptr;
246  c->state = STATE_OPTION_SENT;
247  break;
248  case STATE_SEND_NLST:
249  c->state = STATE_NLST_SENT;
250  break;
251  case STATE_SEND_RETR:
252  c->state = STATE_RETR_SENT;
253  break;
254  case STATE_SEND_CWD:
255  c->state = STATE_CWD_SENT;
256  break;
257  case STATE_SEND_CDUP:
258  c->state = STATE_CDUP_SENT;
259  break;
260  case STATE_SEND_QUIT:
261  c->state = STATE_QUIT_SENT;
262  uip_close();
263  break;
264  }
265 }
266 /*---------------------------------------------------------------------------*/
267 static void
268 senddata(struct ftp_connection *c)
269 {
270  uint16_t len;
271 
272  switch(c->state) {
273  case STATE_SEND_USER:
274  len = 5 + (uint16_t)strlen(ftpc_username()) + 2;
275  strcpy(uip_appdata, "USER ");
276  strncpy((char *)uip_appdata + 5, ftpc_username(), uip_mss() - 5 - 2);
277  strcpy((char *)uip_appdata + len - 2, "\r\n");
278  break;
279  case STATE_SEND_PASS:
280  len = 5 + (uint16_t)strlen(ftpc_password()) + 2;
281  strcpy(uip_appdata, "PASS ");
282  strncpy((char *)uip_appdata + 5, ftpc_password(), uip_mss() - 5 - 2);
283  strcpy((char *)uip_appdata + len - 2, "\r\n");
284  break;
285  case STATE_SEND_PORT:
286  len = sprintf(uip_appdata, "PORT %d,%d,%d,%d,%d,%d\n",
287  uip_ipaddr_to_quad(&uip_hostaddr),
288  (c->dataconn.port) >> 8,
289  (c->dataconn.port) & 0xff);
290  break;
291  case STATE_SEND_OPTIONS:
292  len = (uint16_t)strlen(options.commands[c->optionsptr]);
293  strcpy(uip_appdata, options.commands[c->optionsptr]);
294  break;
295  case STATE_SEND_NLST:
296  len = 6;
297  strcpy(uip_appdata, "NLST\r\n");
298  break;
299  case STATE_SEND_RETR:
300  len = sprintf(uip_appdata, "RETR %s\r\n", c->filename);
301  break;
302  case STATE_SEND_CWD:
303  len = sprintf(uip_appdata, "CWD %s\r\n", c->filename);
304  break;
305  case STATE_SEND_CDUP:
306  len = 6;
307  strcpy(uip_appdata, "CDUP\r\n");
308  break;
309  case STATE_SEND_QUIT:
310  len = 6;
311  strcpy(uip_appdata, "QUIT\r\n");
312  break;
313  default:
314  return;
315  }
316 
317  petsciiconv_toascii(uip_appdata, len);
318  uip_send(uip_appdata, len);
319 }
320 /*---------------------------------------------------------------------------*/
321 void
322 ftpc_appcall(void *state)
323 {
324  int i, t;
325  struct ftp_connection *c = (struct ftp_connection *)state;
326  struct ftp_dataconn *d = (struct ftp_dataconn *)state;
327 
328  if(uip_connected()) {
329  if(state == NULL) {
330  if(waiting_for_dataconn != NULL) {
331  d = &waiting_for_dataconn->dataconn;
332  waiting_for_dataconn = NULL;
333  tcp_markconn(uip_conn, d);
334  d->filenameptr = 0;
335 
336  } else {
337  uip_abort();
338  }
339  } else {
340  /* tcp_listen(uip_conn->lport);*/
341  senddata(c);
342  }
343  return;
344  }
345 
346  if(c->type == TYPE_ABORT) {
347  uip_abort();
348  return;
349  }
350 
351  if(c->type == TYPE_CLOSE) {
352  uip_close();
353  c->type = TYPE_CONTROL;
354  return;
355  }
356 
357  if(c->type == TYPE_CONTROL) {
358  if(uip_closed()) {
359  c->dataconn.type = TYPE_ABORT;
360  ftpc_closed();
361  memb_free(&connections, c);
362  }
363  if(uip_aborted()) {
364  c->dataconn.type = TYPE_ABORT;
365  ftpc_aborted();
366  memb_free(&connections, c);
367  }
368  if(uip_timedout()) {
369  c->dataconn.type = TYPE_ABORT;
370  ftpc_timedout();
371  memb_free(&connections, c);
372  }
373 
374 
375  if(uip_acked()) {
376  acked(c);
377  }
378  if(uip_newdata()) {
379  newdata(c);
380  }
381  if(uip_rexmit() ||
382  uip_newdata() ||
383  uip_acked()) {
384  senddata(c);
385  } else if(uip_poll()) {
386  senddata(c);
387  }
388  } else {
389  if(d->conntype == CONNTYPE_LIST) {
390  if(uip_newdata()) {
391  for(i = 0; i < uip_datalen(); ++i) {
392  t = ((char *)uip_appdata)[i];
393 
394  if(d->filenameptr < sizeof(d->filename) - 1 &&
395  t != ISO_cr &&
396  t != ISO_nl) {
397  d->filename[d->filenameptr] = t;
398  ++d->filenameptr;
399  }
400 
401  if(t == ISO_nl) {
402  d->filename[d->filenameptr] = 0;
403  petsciiconv_topetscii(d->filename, d->filenameptr);
404  ftpc_list_file(d->filename);
405  d->filenameptr = 0;
406  }
407 
408  }
409  }
410  if(uip_closed()) {
411  ftpc_list_file(NULL);
412  }
413  } else {
414  if(uip_newdata()) {
415  ftpc_data(uip_appdata, uip_datalen());
416  /* printf("Received %d data bytes: '%s'\n",
417  uip_datalen(), uip_appdata);*/
418  } else if(uip_closed() || uip_timedout() || uip_aborted()) {
419  ftpc_data(NULL, 0);
420  }
421  }
422  }
423 }
424 /*---------------------------------------------------------------------------*/
425 char
426 ftpc_list(void *conn)
427 {
428  struct ftp_connection *c;
429 
430  c = conn;
431 
432  if(c == NULL ||
433  c->state != STATE_CONNECTED) {
434  return 0;
435  }
436 
437  c->state = STATE_SEND_NLST;
438  c->dataconn.conntype = CONNTYPE_LIST;
439  waiting_for_dataconn = c;
440  return 1;
441 }
442 /*---------------------------------------------------------------------------*/
443 char
444 ftpc_get(void *conn, char *filename)
445 {
446  struct ftp_connection *c;
447 
448  c = conn;
449 
450  if(c == NULL ||
451  c->state != STATE_CONNECTED) {
452  return 0;
453  }
454 
455  strncpy(c->filename, filename, sizeof(c->filename));
456  petsciiconv_toascii(c->filename, sizeof(c->filename));
457 
458  c->state = STATE_SEND_RETR;
459  c->dataconn.conntype = CONNTYPE_FILE;
460  waiting_for_dataconn = c;
461  return 1;
462 }
463 /*---------------------------------------------------------------------------*/
464 void
465 ftpc_close(void *conn)
466 {
467  struct ftp_connection *c;
468 
469  c = conn;
470 
471  if(c == NULL) {
472  return;
473  }
474 
475  c->type = TYPE_CLOSE;
476 }
477 /*---------------------------------------------------------------------------*/
478 void
479 ftpc_cwd(void *conn, char *dirname)
480 {
481  struct ftp_connection *c;
482 
483  c = conn;
484 
485  if(c == NULL ||
486  c->state != STATE_CONNECTED) {
487  return;
488  }
489 
490  strncpy(c->filename, dirname, sizeof(c->filename));
491  c->state = STATE_SEND_CWD;
492 }
493 /*---------------------------------------------------------------------------*/
494 void
495 ftpc_cdup(void *conn)
496 {
497  struct ftp_connection *c;
498 
499  c = conn;
500 
501  if(c == NULL ||
502  c->state != STATE_CONNECTED) {
503  return;
504  }
505 
506  c->state = STATE_SEND_CDUP;
507 }
508 /*---------------------------------------------------------------------------*/
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
Definition: memb.c:52
Representation of a uIP TCP connection.
Definition: uip.h:1336
CCIF struct uip_conn * tcp_connect(uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
Open a TCP connection to the specified IP address and port.
#define uip_aborted()
Has the connection been aborted by the other end?
Definition: uip.h:781
CCIF void uip_send(const void *data, int len)
Send data on the current connection.
Definition: uip6.c:2310
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:738
char memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:79
#define uip_mss()
Get the current maximum segment size that can be sent on the current connection.
Definition: uip.h:838
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
#define NULL
The null pointer.
#define uip_poll()
Is the connection being polled by uIP?
Definition: uip.h:817
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
Definition: uip.h:1238
CCIF void tcp_unlisten(uint16_t port)
Close a listening TCP port.
#define uip_acked()
Has previously sent data been acknowledged?
Definition: uip.h:749
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:761
#define uip_abort()
Abort the current connection.
Definition: uip.h:682
PETSCII/ASCII conversion functions.
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:89
CCIF uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
Definition: uip6.c:2298
#define uip_ipaddr_to_quad(a)
Convert an IP address to four bytes separated by commas.
Definition: uip.h:927
#define uip_close()
Close the current connection.
Definition: uip.h:671
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:651
#define uip_timedout()
Has the connection timed out?
Definition: uip.h:791
CCIF void tcp_listen(uint16_t port)
Open a TCP port.
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:771
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74
#define uip_rexmit()
Do we need to retransmit previously data?
Definition: uip.h:803