1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-21 23:58:49 -05:00

John Kelly added TFTP support to libcurl. A bunch of new error codes was

added. TODO: add them to docs. add TFTP server to test suite. add TFTP to
list of protocols whereever those are mentioned.
This commit is contained in:
Daniel Stenberg 2005-09-02 15:11:08 +00:00
parent 911d135deb
commit 56d9624b56
13 changed files with 863 additions and 8 deletions

View File

@ -7,6 +7,11 @@
Changelog Changelog
Daniel (1 September 2005)
- John Kelly added TFTP support to libcurl. A bunch of new error codes was
added. TODO: add them to docs. add TFTP server to test suite. add TFTP to
list of protocols whereever those are mentioned.
Version 7.14.1 (1 September 2005) Version 7.14.1 (1 September 2005)
Daniel (29 August 2005) Daniel (29 August 2005)

View File

@ -251,6 +251,21 @@ AC_HELP_STRING([--disable-telnet],[Disable TELNET support]),
esac ], esac ],
AC_MSG_RESULT(yes) AC_MSG_RESULT(yes)
) )
AC_MSG_CHECKING([whether to support tftp])
AC_ARG_ENABLE(tftp,
AC_HELP_STRING([--enable-tftp],[Enable TFTP support])
AC_HELP_STRING([--disable-tftp],[Disable TFTP support]),
[ case "$enableval" in
no)
AC_MSG_RESULT(no)
AC_DEFINE(CURL_DISABLE_TFTP, 1, [to disable TFTP])
AC_SUBST(CURL_DISABLE_TFTP, [1])
;;
*) AC_MSG_RESULT(yes)
;;
esac ],
AC_MSG_RESULT(yes)
)
dnl ********************************************************************** dnl **********************************************************************
dnl Check for built-in manual dnl Check for built-in manual

View File

@ -309,6 +309,13 @@ typedef enum {
CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */
CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not
accepted and we failed to login */ accepted and we failed to login */
CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */
CURLE_TFTP_PERM, /* 69 - permission problem on server */
CURLE_TFTP_DISKFULL, /* 70 - out of disk space on server */
CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */
CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */
CURLE_TFTP_EXISTS, /* 73 - File already exists */
CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */
CURL_LAST /* never use! */ CURL_LAST /* never use! */
} CURLcode; } CURLcode;

View File

@ -8,7 +8,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
content_encoding.c share.c http_digest.c md5.c http_negotiate.c \ content_encoding.c share.c http_digest.c md5.c http_negotiate.c \
http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \ http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \
hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c \ hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c \
select.c gtls.c sslgen.c select.c gtls.c sslgen.c tftp.c
HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \
progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \
@ -18,5 +18,5 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \
share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h \ share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h \
inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h \ inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h \
setup.h transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h \ setup.h transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h \
gtls.h gtls.h tftp.h

View File

@ -660,7 +660,12 @@ singleipconnect(struct connectdata *conn,
/* set socket non-blocking */ /* set socket non-blocking */
Curl_nonblock(sockfd, TRUE); Curl_nonblock(sockfd, TRUE);
/* Connect TCP sockets, bind UDP */
if(ai->ai_socktype==SOCK_STREAM) {
rc = connect(sockfd, ai->ai_addr, (socklen_t)ai->ai_addrlen); rc = connect(sockfd, ai->ai_addr, (socklen_t)ai->ai_addrlen);
} else {
rc = 0;
}
if(-1 == rc) { if(-1 == rc) {
error = Curl_ourerrno(); error = Curl_ourerrno();

View File

@ -423,7 +423,11 @@ Curl_addrinfo *Curl_he2ai(struct hostent *he, int port)
prevai->ai_next = ai; prevai->ai_next = ai;
ai->ai_family = AF_INET; /* we only support this */ ai->ai_family = AF_INET; /* we only support this */
ai->ai_socktype = SOCK_STREAM; /* we only support this */ if(port == PORT_TFTP)
ai->ai_socktype = SOCK_DGRAM;
else
ai->ai_socktype = SOCK_STREAM;
ai->ai_addrlen = sizeof(struct sockaddr_in); ai->ai_addrlen = sizeof(struct sockaddr_in);
/* make the ai_addr point to the address immediately following this struct /* make the ai_addr point to the address immediately following this struct
and use that area to store the address */ and use that area to store the address */

View File

@ -251,7 +251,12 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_family = pf; hints.ai_family = pf;
if(conn->protocol & PROT_TFTP)
hints.ai_socktype = SOCK_DGRAM;
else
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = ai_flags; hints.ai_flags = ai_flags;
snprintf(sbuf, sizeof(sbuf), "%d", port); snprintf(sbuf, sizeof(sbuf), "%d", port);
error = getaddrinfo(hostname, sbuf, &hints, &res); error = getaddrinfo(hostname, sbuf, &hints, &res);

View File

@ -245,6 +245,27 @@ curl_easy_strerror(CURLcode error)
case CURLE_LOGIN_DENIED: case CURLE_LOGIN_DENIED:
return "FTP: login denied";; return "FTP: login denied";;
case CURLE_TFTP_NOTFOUND:
return "TFTP: File Not Found";;
case CURLE_TFTP_PERM:
return "TFTP: Access Violation";;
case CURLE_TFTP_DISKFULL:
return "TFTP: Disk full or allocation exceeded";;
case CURLE_TFTP_ILLEGAL:
return "TFTP: Illegal operation";;
case CURLE_TFTP_UNKNOWNID:
return "TFTP: Unknown transfer ID";;
case CURLE_TFTP_EXISTS:
return "TFTP: File already exists";;
case CURLE_TFTP_NOSUCHUSER:
return "TFTP: No such user";;
case CURLE_URL_MALFORMAT_USER: /* not used by current libcurl */ case CURLE_URL_MALFORMAT_USER: /* not used by current libcurl */
case CURLE_MALFORMAT_USER: /* not used by current libcurl */ case CURLE_MALFORMAT_USER: /* not used by current libcurl */
case CURLE_BAD_CALLING_ORDER: /* not used by current libcurl */ case CURLE_BAD_CALLING_ORDER: /* not used by current libcurl */

720
lib/tftp.c Normal file
View File

@ -0,0 +1,720 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#include "setup.h"
#ifndef CURL_DISABLE_TFTP
/* -- WIN32 approved -- */
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <errno.h>
#if defined(WIN32)
#include <time.h>
#include <io.h>
#else
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <netinet/in.h>
#include <sys/time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <netdb.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#include <sys/ioctl.h>
#include <signal.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#endif
#include "urldata.h"
#include <curl/curl.h>
#include "transfer.h"
#include "sendf.h"
#include "tftp.h"
#include "progress.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
#include "memory.h"
#include "select.h"
/* The last #include file should be: */
#include "memdebug.h"
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
typedef enum {
TFTP_MODE_NETASCII=0,
TFTP_MODE_OCTET
} tftp_mode_t;
typedef enum {
TFTP_STATE_START=0,
TFTP_STATE_RX,
TFTP_STATE_TX,
TFTP_STATE_FIN
} tftp_state_t;
typedef enum {
TFTP_EVENT_INIT=0,
TFTP_EVENT_RRQ = 1,
TFTP_EVENT_WRQ = 2,
TFTP_EVENT_DATA = 3,
TFTP_EVENT_ACK = 4,
TFTP_EVENT_ERROR = 5,
TFTP_EVENT_TIMEOUT
} tftp_event_t;
typedef enum {
TFTP_ERR_UNDEF=0,
TFTP_ERR_NOTFOUND,
TFTP_ERR_PERM,
TFTP_ERR_DISKFULL,
TFTP_ERR_ILLEGAL,
TFTP_ERR_UNKNOWNID,
TFTP_ERR_EXISTS,
TFTP_ERR_NOSUCHUSER,
TFTP_ERR_TIMEOUT,
TFTP_ERR_NORESPONSE
} tftp_error_t;
typedef struct tftp_packet {
unsigned short event;
union {
struct {
unsigned char data[512];
} request;
struct {
unsigned short block;
unsigned char data[512];
} data;
struct {
unsigned short block;
} ack;
struct {
unsigned short code;
unsigned char data[512];
} error;
} u;
} tftp_packet_t;
typedef struct tftp_state_data {
tftp_state_t state;
tftp_mode_t mode;
tftp_error_t error;
struct connectdata *conn;
int sockfd;
int retries;
int retry_time;
int retry_max;
time_t start_time;
time_t max_time;
unsigned short block;
struct sockaddr local_addr;
socklen_t local_addrlen;
struct sockaddr remote_addr;
socklen_t remote_addrlen;
int rbytes;
int sbytes;
tftp_packet_t rpacket;
tftp_packet_t spacket;
} tftp_state_data_t;
/* Forward declarations */
static void tftp_rx(tftp_state_data_t *state, tftp_event_t event) ;
static void tftp_tx(tftp_state_data_t *state, tftp_event_t event) ;
void tftp_set_timeouts(tftp_state_data_t *state) ;
/**********************************************************
*
* tftp_set_timeouts -
*
* Set timeouts based on state machine state.
* Use user provided connect timeouts until DATA or ACK
* packet is received, then use user-provided transfer timeouts
*
*
**********************************************************/
void tftp_set_timeouts(tftp_state_data_t *state)
{
struct SessionHandle *data = state->conn->data;
unsigned long maxtime, timeout;
time(&state->start_time);
if(state->state == TFTP_STATE_START) {
/* Compute drop-dead time */
maxtime = data->set.connecttimeout?data->set.connecttimeout:30;
state->max_time = state->start_time+maxtime;
/* Set per-block timeout to total */
timeout = maxtime ;
/* Average restart after 5 seconds */
state->retry_max = timeout/5;
/* Compute the re-start interval to suit the timeout */
state->retry_time = timeout/state->retry_max;
if(state->retry_time<1) state->retry_time=1;
}
else {
/* Compute drop-dead time */
maxtime = data->set.timeout?data->set.timeout:3600;
state->max_time = state->start_time+maxtime;
/* Set per-block timeout to 10% of total */
timeout = maxtime/10 ;
/* Average reposting an ACK after 15 seconds */
state->retry_max = timeout/15;
}
/* But bound the total number */
if(state->retry_max<3) state->retry_max=3;
if(state->retry_max>50) state->retry_max=50;
/* Compute the re-ACK interval to suit the timeout */
state->retry_time = timeout/state->retry_max;
if(state->retry_time<1) state->retry_time=1;
infof(data, "set timeouts for state %d; Total %d, retry %d maxtry %d\n",
state->state, (state->max_time-state->start_time),
state->retry_time, state->retry_max);
}
/**********************************************************
*
* tftp_set_send_first
*
* Event handler for the START state
*
**********************************************************/
static void tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
{
int sbytes;
const char *mode = "octet";
char *filename = state->conn->path;
struct SessionHandle *data = state->conn->data;
/* Set ascii mode if -B flag was used */
if(data->set.ftp_ascii)
mode = "netascii";
switch(event) {
case TFTP_EVENT_INIT: /* Send the first packet out */
case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */
/* Increment the retry counter, quit if over the limit */
state->retries++;
if(state->retries>state->retry_max) {
state->error = TFTP_ERR_NORESPONSE;
state->state = TFTP_STATE_FIN;
return;
}
if(data->set.upload) {
/* If we are uploading, send an WRQ */
state->spacket.event = htons(TFTP_EVENT_WRQ);
filename = curl_unescape(filename, strlen(filename));
state->conn->upload_fromhere = (char *)state->spacket.u.data.data;
if(data->set.infilesize != -1) {
Curl_pgrsSetUploadSize(data, data->set.infilesize);
}
}
else {
/* If we are downloading, send an RRQ */
state->spacket.event = htons(TFTP_EVENT_RRQ);
}
sprintf((char *)state->spacket.u.request.data, "%s%c%s%c",
filename, '\0', mode, '\0');
sbytes = 4 + strlen(filename) + strlen(mode);
sbytes = sendto(state->sockfd, &state->spacket, sbytes, 0,
state->conn->ip_addr->ai_addr,
state->conn->ip_addr->ai_addrlen);
if(sbytes < 0) {
failf(data, "%s\n", strerror(errno));
}
break;
case TFTP_EVENT_ACK: /* Connected for transmit */
infof(data, "%s\n", "Connected for transmit");
state->state = TFTP_STATE_TX;
tftp_set_timeouts(state);
tftp_tx(state, event);
break;
case TFTP_EVENT_DATA: /* connected for receive */
infof(data, "%s\n", "Connected for receive");
state->state = TFTP_STATE_RX;
tftp_set_timeouts(state);
tftp_rx(state, event);
break;
case TFTP_EVENT_ERROR:
state->state = TFTP_STATE_FIN;
break;
default:
failf(state->conn->data, "tftp_send_first: internal error\n");
break;
}
}
/**********************************************************
*
* tftp_rx
*
* Event handler for the RX state
*
**********************************************************/
static void tftp_rx(tftp_state_data_t *state, tftp_event_t event)
{
int sbytes;
int rblock;
switch(event) {
case TFTP_EVENT_DATA:
/* Is this the block we expect? */
rblock = ntohs(state->rpacket.u.data.block);
if ((state->block+1) != rblock) {
/* No, log it, up the retry count and fail if over the limit */
infof(state->conn->data,
"Received unexpected DATA packet block %d\n", rblock);
state->retries++;
if (state->retries>state->retry_max) {
failf(state->conn->data, "tftp_rx: giving up waiting for block %d\n",
state->block+1);
return;
}
}
/* This is the expected block. Reset counters and ACK it. */
state->block = rblock;
state->retries = 0;
state->spacket.event = htons(TFTP_EVENT_ACK);
state->spacket.u.ack.block = htons(state->block);
sbytes = sendto(state->sockfd, &state->spacket, 4, MSG_NOSIGNAL,
&state->remote_addr, state->remote_addrlen);
if(sbytes < 0) {
failf(state->conn->data, "%s\n", strerror(errno));
}
/* Check if completed (That is, a less than full packet is recieved) */
if (state->rbytes < (int)sizeof(state->spacket)){
state->state = TFTP_STATE_FIN;
}
else {
state->state = TFTP_STATE_RX;
}
break;
case TFTP_EVENT_TIMEOUT:
/* Increment the retry count and fail if over the limit */
state->retries++;
infof(state->conn->data,
"Timeout waiting for block %d ACK. Retries = %d\n", state->retries);
if(state->retries > state->retry_max) {
state->error = TFTP_ERR_TIMEOUT;
state->state = TFTP_STATE_FIN;
} else {
/* Resend the previous ACK */
sbytes = sendto(state->sockfd, &state->spacket,
4, MSG_NOSIGNAL,
&state->remote_addr, state->remote_addrlen);
/* Check all sbytes were sent */
if(sbytes<0) {
failf(state->conn->data, "%s\n", strerror(errno));
}
}
break;
case TFTP_EVENT_ERROR:
state->state = TFTP_STATE_FIN;
break;
default:
failf(state->conn->data, "%s\n", "tftp_rx: internal error");
break;
}
Curl_pgrsSetDownloadCounter(state->conn->data,
(curl_off_t) state->block*512);
}
/**********************************************************
*
* tftp_tx
*
* Event handler for the TX state
*
**********************************************************/
static void tftp_tx(tftp_state_data_t *state, tftp_event_t event)
{
int sbytes;
int rblock;
switch(event) {
case TFTP_EVENT_ACK:
/* Ack the packet */
rblock = ntohs(state->rpacket.u.data.block);
if(rblock != state->block) {
/* This isn't the expected block. Log it and up the retry counter */
infof(state->conn->data, "Received ACK for block %d, expecting %d\n",
rblock, state->block);
state->retries++;
/* Bail out if over the maximum */
if(state->retries>state->retry_max) {
failf(state->conn->data, "%s\n",
"tftp_tx: giving up waiting for block %d ack",
state->block);
}
return;
}
/* This is the expected packet. Reset the counters and send the next
block */
state->block++;
state->retries = 0;
state->spacket.event = htons(TFTP_EVENT_DATA);
state->spacket.u.ack.block = htons(state->block);
if(state->block > 1 && state->sbytes < 512) {
state->state = TFTP_STATE_FIN;
return;
}
Curl_fillreadbuffer(state->conn, 512, &state->sbytes);
sbytes = sendto(state->sockfd, &state->spacket,
4+state->sbytes, MSG_NOSIGNAL,
&state->remote_addr, state->remote_addrlen);
/* Check all sbytes were sent */
if(sbytes<0) {
failf(state->conn->data, "%s\n", strerror(errno));
}
break;
case TFTP_EVENT_TIMEOUT:
/* Increment the retry counter and log the timeout */
state->retries++;
infof(state->conn->data, "Timeout waiting for block %d ACK. "
" Retries = %d\n", state->retries);
/* Decide if we've had enough */
if(state->retries > state->retry_max) {
state->error = TFTP_ERR_TIMEOUT;
state->state = TFTP_STATE_FIN;
} else {
/* Re-send the data packet */
sbytes = sendto(state->sockfd, &state->spacket,
4+state->sbytes, MSG_NOSIGNAL,
&state->remote_addr, state->remote_addrlen);
/* Check all sbytes were sent */
if(sbytes<0) {
failf(state->conn->data, "%s\n", strerror(errno));
}
}
break;
case TFTP_EVENT_ERROR:
state->state = TFTP_STATE_FIN;
break;
default:
failf(state->conn->data, "%s\n", "tftp_tx: internal error");
break;
}
/* Update the progress meter */
Curl_pgrsSetUploadCounter(state->conn->data, (curl_off_t) state->block*512);
}
/**********************************************************
*
* tftp_state_machine
*
* The tftp state machine event dispatcher
*
**********************************************************/
static CURLcode tftp_state_machine(tftp_state_data_t *state,
tftp_event_t event)
{
switch(state->state) {
case TFTP_STATE_START:
tftp_send_first(state, event);
break;
case TFTP_STATE_RX:
tftp_rx(state, event);
break;
case TFTP_STATE_TX:
tftp_tx(state, event);
break;
case TFTP_STATE_FIN:
infof(state->conn->data, "%s\n", "TFTP finished");
break;
default:
failf(state->conn->data, "%s\n", "Internal state machine error");
break;
}
return CURLE_OK;
}
/**********************************************************
*
* Curl_tftp_connect
*
* The connect callback
*
**********************************************************/
CURLcode Curl_tftp_connect(struct connectdata *conn, bool *done)
{
CURLcode code;
tftp_state_data_t *state;
int rc;
if((state = conn->proto.tftp = calloc(sizeof(tftp_state_data_t), 1))==NULL) {
return CURLE_OUT_OF_MEMORY;
}
state->conn = conn;
state->sockfd = state->conn->sock[FIRSTSOCKET];
state->state = TFTP_STATE_START;
tftp_set_timeouts(state);
/* Bind to any interface, random UDP port */
rc = bind(state->sockfd, &state->local_addr, sizeof(state->local_addr));
Curl_pgrsStartNow(conn->data);
*done = TRUE;
code = CURLE_OK;
return(code);
}
/**********************************************************
*
* Curl_tftp_done
*
* The done callback
*
**********************************************************/
CURLcode Curl_tftp_done(struct connectdata *conn, CURLcode status)
{
(void)status; /* unused */
free(conn->proto.tftp);
conn->proto.tftp = NULL;
Curl_pgrsDone(conn);
return CURLE_OK;
}
/**********************************************************
*
* Curl_tftp
*
* The do callback
*
* This callback handles the entire TFTP transfer
*
**********************************************************/
CURLcode Curl_tftp(struct connectdata *conn, bool *done)
{
struct SessionHandle *data = conn->data;
tftp_state_data_t *state = (tftp_state_data_t *)(conn->proto.tftp);
tftp_event_t event;
fd_set readset;
struct timeval tv;
CURLcode code;
int rc;
struct sockaddr fromaddr;
socklen_t fromlen;
int check_time = 0;
(void)done; /* prevent compiler warning */
/* Run the TFTP State Machine */
for(tftp_state_machine(state, TFTP_EVENT_INIT);
state->state != TFTP_STATE_FIN;
tftp_state_machine(state, event) ) {
/* Update the progress meter */
Curl_pgrsUpdate(conn);
/* Waiting on event from network or OS */
FD_ZERO(&readset);
FD_SET(state->sockfd, &readset);
tv.tv_sec=state->retry_time; tv.tv_usec=0;
restart:
/* Wait until ready to read or timeout occurs */
rc=select(state->sockfd+1, &readset, NULL, NULL, &tv);
if(rc == -1) {
/* Restart if a signal interrupt occured */
if(errno == EINTR) {
goto restart;
}
/* Otherwise, bail out */
failf(state->conn->data, "%s\n", strerror(errno));
event = TFTP_EVENT_ERROR;
}
else if (rc==0) {
/* A timeout occured */
event = TFTP_EVENT_TIMEOUT;
/* Force a look at transfer timeouts */
check_time = 0;
}
else {
/* Receive the packet */
fromlen=sizeof(fromaddr);
state->rbytes = recvfrom(state->sockfd,
(void *)&state->rpacket, sizeof(state->rpacket),
0, &fromaddr, &fromlen);
if(state->remote_addrlen==0) {
memcpy(&state->remote_addr, &fromaddr, fromlen);
state->remote_addrlen = fromlen;
}
/* The event is given by the TFTP packet time */
event = ntohs(state->rpacket.event);
switch(event) {
case TFTP_EVENT_DATA:
Curl_client_write(data, CLIENTWRITE_BODY,
(char *)state->rpacket.u.data.data, state->rbytes-4);
break;
case TFTP_EVENT_ERROR:
state->error = ntohs(state->rpacket.u.error.code);
infof(conn->data, "%s\n", (char *)state->rpacket.u.error.data);
break;
case TFTP_EVENT_ACK:
break;
case TFTP_EVENT_RRQ:
case TFTP_EVENT_WRQ:
default:
failf(conn->data, "%s\n", "Internal error: Unexpected packet");
break;
}
}
/* Check for transfer timeout every 10 blocks, or after timeout */
if(check_time%10==0) {
time_t current;
time(&current);
if(current>state->max_time) {
state->error = TFTP_ERR_TIMEOUT;
state->state = TFTP_STATE_FIN;
}
}
}
/* Tell curl we're done */
Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
/* If we have encountered an error */
if(state->error) {
/* Translate internal error codes to curl error codes */
switch(state->error) {
case TFTP_ERR_NOTFOUND:
code = CURLE_TFTP_NOTFOUND;
break;
case TFTP_ERR_PERM:
code = CURLE_TFTP_PERM;
break;
case TFTP_ERR_DISKFULL:
code = CURLE_TFTP_DISKFULL;
break;
case TFTP_ERR_ILLEGAL:
code = CURLE_TFTP_ILLEGAL;
break;
case TFTP_ERR_UNKNOWNID:
code = CURLE_TFTP_UNKNOWNID;
break;
case TFTP_ERR_EXISTS:
code = CURLE_TFTP_EXISTS;
break;
case TFTP_ERR_NOSUCHUSER:
code = CURLE_TFTP_NOSUCHUSER;
break;
case TFTP_ERR_TIMEOUT:
code = CURLE_OPERATION_TIMEOUTED;
break;
case TFTP_ERR_NORESPONSE:
code = CURLE_COULDNT_CONNECT;
break;
default:
code= CURLE_ABORTED_BY_CALLBACK;
break;
}
}
else
code = CURLE_OK;
return code;
}
#endif

31
lib/tftp.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef __TFTP_H
#define __TFTP_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#ifndef CURL_DISABLE_TFTP
CURLcode Curl_tftp_connect(struct connectdata *conn, bool *done);
CURLcode Curl_tftp(struct connectdata *conn, bool *done);
CURLcode Curl_tftp_done(struct connectdata *conn, CURLcode);
#endif
#endif

View File

@ -77,9 +77,7 @@
#error "We can't compile without socket() support!" #error "We can't compile without socket() support!"
#endif #endif
#endif #endif
#ifdef USE_LIBIDN #ifdef USE_LIBIDN
#include <idna.h> #include <idna.h>
#include <tld.h> #include <tld.h>
@ -123,6 +121,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
#include "ftp.h" #include "ftp.h"
#include "dict.h" #include "dict.h"
#include "telnet.h" #include "telnet.h"
#include "tftp.h"
#include "http.h" #include "http.h"
#include "file.h" #include "file.h"
#include "ldap.h" #include "ldap.h"
@ -2915,6 +2914,43 @@ static CURLcode CreateConnection(struct SessionHandle *data,
#else #else
failf(data, LIBCURL_NAME failf(data, LIBCURL_NAME
" was built with FILE disabled!"); " was built with FILE disabled!");
#endif
}
else if (strequal(conn->protostr, "TFTP")) {
#ifndef CURL_DISABLE_TFTP
char *type;
conn->protocol |= PROT_TFTP;
conn->port = PORT_TFTP;
conn->remote_port = PORT_TFTP;
conn->curl_connect = Curl_tftp_connect;
conn->curl_do = Curl_tftp;
conn->curl_done = Curl_tftp_done;
/* TFTP URLs support an extension like ";mode=<typecode>" that
* we'll try to get now! */
type=strstr(conn->path, ";mode=");
if(!type) {
type=strstr(conn->host.rawalloc, ";mode=");
}
if(type) {
char command;
*type=0; /* it was in the middle of the hostname */
command = toupper((int)type[6]);
switch(command) {
case 'A': /* ASCII mode */
case 'N': /* NETASCII mode */
data->set.ftp_ascii = 1;
break;
case 'O': /* octet mode */
case 'I': /* binary mode */
default:
/* switch off ASCII */
data->set.ftp_ascii = 0;
break;
}
}
#else
failf(data, LIBCURL_NAME
" was built with TFTP disabled!");
#endif #endif
} }
else { else {

View File

@ -35,6 +35,7 @@
#define PORT_HTTPS 443 #define PORT_HTTPS 443
#define PORT_DICT 2628 #define PORT_DICT 2628
#define PORT_LDAP 389 #define PORT_LDAP 389
#define PORT_TFTP 69
#define DICT_MATCH "/MATCH:" #define DICT_MATCH "/MATCH:"
#define DICT_MATCH2 "/M:" #define DICT_MATCH2 "/M:"
@ -540,6 +541,7 @@ struct connectdata {
#define PROT_DICT (1<<6) #define PROT_DICT (1<<6)
#define PROT_LDAP (1<<7) #define PROT_LDAP (1<<7)
#define PROT_FILE (1<<8) #define PROT_FILE (1<<8)
#define PROT_TFTP (1<<11)
#define PROT_FTPS (1<<9) #define PROT_FTPS (1<<9)
#define PROT_SSL (1<<10) /* protocol requires SSL */ #define PROT_SSL (1<<10) /* protocol requires SSL */
@ -695,6 +697,7 @@ struct connectdata {
struct HTTP *gopher; /* alias, just for the sake of being more readable */ struct HTTP *gopher; /* alias, just for the sake of being more readable */
struct HTTP *https; /* alias, just for the sake of being more readable */ struct HTTP *https; /* alias, just for the sake of being more readable */
struct FTP *ftp; struct FTP *ftp;
void *tftp; /* private for tftp.c-eyes only */
struct FILEPROTO *file; struct FILEPROTO *file;
void *telnet; /* private for telnet.c-eyes only */ void *telnet; /* private for telnet.c-eyes only */
void *generic; void *generic;

View File

@ -81,6 +81,9 @@ char *curl_version(void)
/* data for curl_version_info */ /* data for curl_version_info */
static const char * const protocols[] = { static const char * const protocols[] = {
#ifndef CURL_DISABLE_TFTP
"tftp",
#endif
#ifndef CURL_DISABLE_FTP #ifndef CURL_DISABLE_FTP
"ftp", "ftp",
#endif #endif