Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
T
TLMSP curl
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
CYBER - Cyber Security
TS 103 523 MSP
TLMSP
TLMSP curl
Commits
cd5de5c0
Commit
cd5de5c0
authored
19 years ago
by
Daniel Stenberg
Browse files
Options
Downloads
Patches
Plain Diff
First version of the TFTP server. Basic functionality is there.
parent
bd5afc26
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
tests/server/tftpd.c
+865
-0
865 additions, 0 deletions
tests/server/tftpd.c
with
865 additions
and
0 deletions
tests/server/tftpd.c
0 → 100644
+
865
−
0
View file @
cd5de5c0
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* $Id$
*
* Trivial file transfer protocol server.
*
* This code includes many modifications by Jim Guyton <guyton@rand-unix>
*
* This source file was started based on netkit-tftpd 0.17
* Heavily modified for curl's test suite
*/
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include
"setup.h"
/* portability help from the lib directory */
#include
<sys/types.h>
#include
<sys/ioctl.h>
#include
<sys/stat.h>
#include
<signal.h>
#include
<fcntl.h>
#include
<sys/socket.h>
#include
<netinet/in.h>
#include
<arpa/tftp.h>
#include
<netdb.h>
#include
<setjmp.h>
#include
<stdio.h>
#include
<errno.h>
#include
<ctype.h>
#include
<string.h>
#include
<stdlib.h>
#include
<unistd.h>
#include
<pwd.h>
#include
<grp.h>
#include
"getpart.h"
#include
"util.h"
struct
testcase
{
char
*
buffer
;
/* holds the file data to send to the client */
size_t
bufsize
;
/* size of the data in buffer */
char
*
rptr
;
/* read pointer into the buffer */
size_t
rcount
;
/* amount of data left to read of the file */
long
num
;
/* test case number */
int
ofile
;
/* file descriptor for output file when uploading to us */
FILE
*
server
;
/* write input "protocol" there for client verification */
};
static
int
synchnet
(
int
);
static
struct
tftphdr
*
r_init
(
void
);
static
struct
tftphdr
*
w_init
(
void
);
static
int
readit
(
struct
testcase
*
test
,
struct
tftphdr
**
dpp
,
int
convert
);
static
int
writeit
(
struct
testcase
*
test
,
struct
tftphdr
**
dpp
,
int
ct
,
int
convert
);
static
void
mysignal
(
int
,
void
(
*
func
)(
int
));
#define TIMEOUT 5
#define PKTSIZE SEGSIZE+4
struct
formats
;
static
int
tftp
(
struct
testcase
*
test
,
struct
tftphdr
*
tp
,
int
size
);
static
void
nak
(
int
error
);
static
void
sendfile
(
struct
testcase
*
test
,
struct
formats
*
pf
);
static
void
recvfile
(
struct
testcase
*
test
,
struct
formats
*
pf
);
static
int
validate_access
(
struct
testcase
*
test
,
const
char
*
,
int
);
static
curl_socket_t
peer
;
static
int
rexmtval
=
TIMEOUT
;
static
int
maxtimeout
=
5
*
TIMEOUT
;
static
char
buf
[
PKTSIZE
];
static
char
ackbuf
[
PKTSIZE
];
static
struct
sockaddr_in
from
;
static
socklen_t
fromlen
;
struct
bf
{
int
counter
;
/* size of data in buffer, or flag */
char
buf
[
PKTSIZE
];
/* room for data packet */
}
bfs
[
2
];
/* Values for bf.counter */
#define BF_ALLOC -3
/* alloc'd but not yet filled */
#define BF_FREE -2
/* free */
/* [-1 .. SEGSIZE] = size of data in the data buffer */
static
int
nextone
;
/* index of next buffer to use */
static
int
current
;
/* index of buffer in use */
/* control flags for crlf conversions */
int
newline
=
0
;
/* fillbuf: in middle of newline expansion */
int
prevchar
=
-
1
;
/* putbuf: previous char (cr check) */
static
void
read_ahead
(
struct
testcase
*
test
,
int
convert
/* if true, convert to ascii */
);
static
int
write_behind
(
struct
testcase
*
test
,
int
convert
);
static
struct
tftphdr
*
rw_init
(
int
);
static
struct
tftphdr
*
w_init
(
void
)
{
return
rw_init
(
0
);
}
/* write-behind */
static
struct
tftphdr
*
r_init
(
void
)
{
return
rw_init
(
1
);
}
/* read-ahead */
static
struct
tftphdr
*
rw_init
(
int
x
)
/* init for either read-ahead or write-behind */
{
/* zero for write-behind, one for read-head */
newline
=
0
;
/* init crlf flag */
prevchar
=
-
1
;
bfs
[
0
].
counter
=
BF_ALLOC
;
/* pass out the first buffer */
current
=
0
;
bfs
[
1
].
counter
=
BF_FREE
;
nextone
=
x
;
/* ahead or behind? */
return
(
struct
tftphdr
*
)
bfs
[
0
].
buf
;
}
/* Have emptied current buffer by sending to net and getting ack.
Free it and return next buffer filled with data.
*/
static
int
readit
(
struct
testcase
*
test
,
struct
tftphdr
**
dpp
,
int
convert
/* if true, convert to ascii */
)
{
struct
bf
*
b
;
bfs
[
current
].
counter
=
BF_FREE
;
/* free old one */
current
=
!
current
;
/* "incr" current */
b
=
&
bfs
[
current
];
/* look at new buffer */
if
(
b
->
counter
==
BF_FREE
)
/* if it's empty */
read_ahead
(
test
,
convert
);
/* fill it */
*
dpp
=
(
struct
tftphdr
*
)
b
->
buf
;
/* set caller's ptr */
return
b
->
counter
;
}
#define MIN(x,y) ((x)<(y)?(x):(y));
/*
* fill the input buffer, doing ascii conversions if requested
* conversions are lf -> cr,lf and cr -> cr, nul
*/
static
void
read_ahead
(
struct
testcase
*
test
,
int
convert
/* if true, convert to ascii */
)
{
int
i
;
char
*
p
;
int
c
;
struct
bf
*
b
;
struct
tftphdr
*
dp
;
b
=
&
bfs
[
nextone
];
/* look at "next" buffer */
if
(
b
->
counter
!=
BF_FREE
)
/* nop if not free */
return
;
nextone
=
!
nextone
;
/* "incr" next buffer ptr */
dp
=
(
struct
tftphdr
*
)
b
->
buf
;
if
(
convert
==
0
)
{
/* The former file reading code did this:
b->counter = read(fileno(file), dp->th_data, SEGSIZE); */
int
copy_n
=
MIN
(
SEGSIZE
,
test
->
rcount
);
memcpy
(
dp
->
th_data
,
test
->
rptr
,
copy_n
);
/* decrease amount, advance pointer */
test
->
rcount
-=
copy_n
;
test
->
rptr
+=
copy_n
;
b
->
counter
=
copy_n
;
return
;
}
p
=
dp
->
th_data
;
for
(
i
=
0
;
i
<
SEGSIZE
;
i
++
)
{
if
(
newline
)
{
if
(
prevchar
==
'\n'
)
c
=
'\n'
;
/* lf to cr,lf */
else
c
=
'\0'
;
/* cr to cr,nul */
newline
=
0
;
}
else
{
if
(
test
->
rcount
)
{
c
=
test
->
rptr
[
0
];
test
->
rptr
++
;
test
->
rcount
--
;
}
else
break
;
if
(
c
==
'\n'
||
c
==
'\r'
)
{
prevchar
=
c
;
c
=
'\r'
;
newline
=
1
;
}
}
*
p
++
=
c
;
}
b
->
counter
=
(
int
)(
p
-
dp
->
th_data
);
}
/* Update count associated with the buffer, get new buffer from the queue.
Calls write_behind only if next buffer not available.
*/
static
int
writeit
(
struct
testcase
*
test
,
struct
tftphdr
**
dpp
,
int
ct
,
int
convert
)
{
bfs
[
current
].
counter
=
ct
;
/* set size of data to write */
current
=
!
current
;
/* switch to other buffer */
if
(
bfs
[
current
].
counter
!=
BF_FREE
)
/* if not free */
write_behind
(
test
,
convert
);
/* flush it */
bfs
[
current
].
counter
=
BF_ALLOC
;
/* mark as alloc'd */
*
dpp
=
(
struct
tftphdr
*
)
bfs
[
current
].
buf
;
return
ct
;
/* this is a lie of course */
}
/*
* Output a buffer to a file, converting from netascii if requested.
* CR,NUL -> CR and CR,LF => LF.
* Note spec is undefined if we get CR as last byte of file or a
* CR followed by anything else. In this case we leave it alone.
*/
static
int
write_behind
(
struct
testcase
*
test
,
int
convert
)
{
char
*
buf
;
int
count
;
int
ct
;
char
*
p
;
int
c
;
/* current character */
struct
bf
*
b
;
struct
tftphdr
*
dp
;
b
=
&
bfs
[
nextone
];
if
(
b
->
counter
<
-
1
)
/* anything to flush? */
return
0
;
/* just nop if nothing to do */
if
(
!
test
->
ofile
)
{
char
outfile
[
256
];
snprintf
(
outfile
,
sizeof
(
outfile
),
"log/upload.%ld"
,
test
->
num
);
test
->
ofile
=
open
(
outfile
,
O_CREAT
|
O_RDWR
,
0777
);
if
(
test
->
ofile
==
-
1
)
{
logmsg
(
"Couldn't create and/or open file %s for upload!"
,
outfile
);
return
-
1
;
/* failure! */
}
}
count
=
b
->
counter
;
/* remember byte count */
b
->
counter
=
BF_FREE
;
/* reset flag */
dp
=
(
struct
tftphdr
*
)
b
->
buf
;
nextone
=
!
nextone
;
/* incr for next time */
buf
=
dp
->
th_data
;
if
(
count
<=
0
)
return
-
1
;
/* nak logic? */
if
(
convert
==
0
)
return
write
(
test
->
ofile
,
buf
,
count
);
p
=
buf
;
ct
=
count
;
while
(
ct
--
)
{
/* loop over the buffer */
c
=
*
p
++
;
/* pick up a character */
if
(
prevchar
==
'\r'
)
{
/* if prev char was cr */
if
(
c
==
'\n'
)
/* if have cr,lf then just */
lseek
(
test
->
ofile
,
-
1
,
SEEK_CUR
);
/* smash lf on top of the cr */
else
if
(
c
==
'\0'
)
/* if have cr,nul then */
goto
skipit
;
/* just skip over the putc */
/* else just fall through and allow it */
}
/* formerly
putc(c, file); */
write
(
test
->
ofile
,
&
c
,
1
);
skipit:
prevchar
=
c
;
}
return
count
;
}
/* When an error has occurred, it is possible that the two sides are out of
* synch. Ie: that what I think is the other side's response to packet N is
* really their response to packet N-1.
*
* So, to try to prevent that, we flush all the input queued up for us on the
* network connection on our host.
*
* We return the number of packets we flushed (mostly for reporting when trace
* is active).
*/
static
int
synchnet
(
curl_socket_t
f
/* socket to flush */
)
{
int
i
,
j
=
0
;
char
rbuf
[
PKTSIZE
];
struct
sockaddr_in
from
;
socklen_t
fromlen
;
while
(
1
)
{
(
void
)
ioctl
(
f
,
FIONREAD
,
&
i
);
if
(
i
)
{
j
++
;
fromlen
=
sizeof
from
;
(
void
)
recvfrom
(
f
,
rbuf
,
sizeof
(
rbuf
),
0
,
(
struct
sockaddr
*
)
&
from
,
&
fromlen
);
}
else
return
j
;
}
}
/*
* Like signal(), but with well-defined semantics.
*/
static
void
mysignal
(
int
sig
,
void
(
*
handler
)(
int
))
{
struct
sigaction
sa
;
memset
(
&
sa
,
0
,
sizeof
(
sa
));
sa
.
sa_handler
=
handler
;
sigaction
(
sig
,
&
sa
,
NULL
);
}
#ifndef DEFAULT_LOGFILE
#define DEFAULT_LOGFILE "log/tftpd.log"
#endif
#define DEFAULT_PORT 8999
/* UDP */
const
char
*
serverlogfile
=
DEFAULT_LOGFILE
;
#define REQUEST_DUMP "log/server.input"
char
use_ipv6
=
FALSE
;
int
main
(
int
argc
,
char
**
argv
)
{
struct
sockaddr_in
me
;
#ifdef ENABLE_IPV6
struct
sockaddr_in6
me6
;
#endif
/* ENABLE_IPV6 */
struct
tftphdr
*
tp
;
int
n
=
0
;
int
arg
=
1
;
FILE
*
pidfile
;
char
*
pidname
=
(
char
*
)
".tftpd.pid"
;
unsigned
short
port
=
DEFAULT_PORT
;
curl_socket_t
sock
;
int
flag
;
int
rc
;
struct
testcase
test
;
while
(
argc
>
arg
)
{
if
(
!
strcmp
(
"--version"
,
argv
[
arg
]))
{
printf
(
"tftpd IPv4%s
\n
"
,
#ifdef ENABLE_IPV6
"/IPv6"
#else
""
#endif
);
return
0
;
}
else
if
(
!
strcmp
(
"--pidfile"
,
argv
[
arg
]))
{
arg
++
;
if
(
argc
>
arg
)
pidname
=
argv
[
arg
++
];
}
else
if
(
!
strcmp
(
"--ipv6"
,
argv
[
arg
]))
{
#ifdef ENABLE_IPV6
use_ipv6
=
TRUE
;
#endif
arg
++
;
}
else
if
(
argc
>
arg
)
{
if
(
atoi
(
argv
[
arg
]))
port
=
(
unsigned
short
)
atoi
(
argv
[
arg
++
]);
if
(
argc
>
arg
)
path
=
argv
[
arg
++
];
}
}
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
win32_init
();
atexit
(
win32_cleanup
);
#endif
#ifdef ENABLE_IPV6
if
(
!
use_ipv6
)
#endif
sock
=
socket
(
AF_INET
,
SOCK_DGRAM
,
0
);
#ifdef ENABLE_IPV6
else
sock
=
socket
(
AF_INET6
,
SOCK_DGRAM
,
0
);
#endif
if
(
sock
<
0
)
{
perror
(
"opening stream socket"
);
logmsg
(
"Error opening socket"
);
exit
(
1
);
}
flag
=
1
;
if
(
setsockopt
(
sock
,
SOL_SOCKET
,
SO_REUSEADDR
,
(
const
void
*
)
&
flag
,
sizeof
(
int
))
<
0
)
{
perror
(
"setsockopt(SO_REUSEADDR)"
);
}
#ifdef ENABLE_IPV6
if
(
!
use_ipv6
)
{
#endif
me
.
sin_family
=
AF_INET
;
me
.
sin_addr
.
s_addr
=
INADDR_ANY
;
me
.
sin_port
=
htons
(
port
);
rc
=
bind
(
sock
,
(
struct
sockaddr
*
)
&
me
,
sizeof
(
me
));
#ifdef ENABLE_IPV6
}
else
{
memset
(
&
me6
,
0
,
sizeof
(
struct
sockaddr_in6
));
me6
.
sin6_family
=
AF_INET6
;
me6
.
sin6_addr
=
in6addr_any
;
me6
.
sin6_port
=
htons
(
port
);
rc
=
bind
(
sock
,
(
struct
sockaddr
*
)
&
me6
,
sizeof
(
me6
));
}
#endif
/* ENABLE_IPV6 */
if
(
rc
<
0
)
{
perror
(
"binding stream socket"
);
logmsg
(
"Error binding socket"
);
exit
(
1
);
}
pidfile
=
fopen
(
pidname
,
"w"
);
if
(
pidfile
)
{
fprintf
(
pidfile
,
"%d
\n
"
,
(
int
)
getpid
());
fclose
(
pidfile
);
}
else
fprintf
(
stderr
,
"Couldn't write pid file
\n
"
);
logmsg
(
"Running IPv%d version on port UDP/%d"
,
#ifdef ENABLE_IPV6
(
use_ipv6
?
6
:
4
)
#else
4
#endif
,
port
);
do
{
FILE
*
server
;
server
=
fopen
(
REQUEST_DUMP
,
"ab"
);
if
(
!
server
)
break
;
fromlen
=
sizeof
(
from
);
n
=
recvfrom
(
sock
,
buf
,
sizeof
(
buf
),
0
,
(
struct
sockaddr
*
)
&
from
,
&
fromlen
);
if
(
n
<
0
)
{
logmsg
(
"recvfrom:
\n
"
);
return
3
;
}
from
.
sin_family
=
AF_INET
;
peer
=
socket
(
AF_INET
,
SOCK_DGRAM
,
0
);
if
(
peer
<
0
)
{
logmsg
(
"socket:
\n
"
);
return
2
;
}
if
(
connect
(
peer
,
(
struct
sockaddr
*
)
&
from
,
sizeof
(
from
))
<
0
)
{
logmsg
(
"connect: fail
\n
"
);
return
1
;
}
tp
=
(
struct
tftphdr
*
)
buf
;
tp
->
th_opcode
=
ntohs
(
tp
->
th_opcode
);
if
(
tp
->
th_opcode
==
RRQ
||
tp
->
th_opcode
==
WRQ
)
{
memset
(
&
test
,
0
,
sizeof
(
test
));
test
.
server
=
server
;
tftp
(
&
test
,
tp
,
n
);
}
fclose
(
server
);
sclose
(
peer
);
}
while
(
1
);
return
0
;
}
struct
formats
{
const
char
*
f_mode
;
int
f_convert
;
}
formats
[]
=
{
{
"netascii"
,
1
},
{
"octet"
,
0
},
{
NULL
,
0
}
};
/*
* Handle initial connection protocol.
*/
static
int
tftp
(
struct
testcase
*
test
,
struct
tftphdr
*
tp
,
int
size
)
{
char
*
cp
;
int
first
=
1
,
ecode
;
struct
formats
*
pf
;
char
*
filename
,
*
mode
=
NULL
;
/* store input protocol */
fprintf
(
test
->
server
,
"opcode: %x
\n
"
,
tp
->
th_opcode
);
filename
=
cp
=
tp
->
th_stuff
;
again:
while
(
cp
<
buf
+
size
)
{
if
(
*
cp
==
'\0'
)
break
;
cp
++
;
}
if
(
*
cp
)
{
nak
(
EBADOP
);
return
3
;
}
if
(
first
)
{
mode
=
++
cp
;
first
=
0
;
goto
again
;
}
/* store input protocol */
fprintf
(
test
->
server
,
"filename: %s
\n
"
,
filename
);
for
(
cp
=
mode
;
*
cp
;
cp
++
)
if
(
isupper
(
*
cp
))
*
cp
=
tolower
(
*
cp
);
/* store input protocol */
fprintf
(
test
->
server
,
"mode: %s
\n
"
,
mode
);
for
(
pf
=
formats
;
pf
->
f_mode
;
pf
++
)
if
(
strcmp
(
pf
->
f_mode
,
mode
)
==
0
)
break
;
if
(
!
pf
->
f_mode
)
{
nak
(
EBADOP
);
return
2
;
}
ecode
=
validate_access
(
test
,
filename
,
tp
->
th_opcode
);
if
(
ecode
)
{
nak
(
ecode
);
return
1
;
}
if
(
tp
->
th_opcode
==
WRQ
)
recvfile
(
test
,
pf
);
else
sendfile
(
test
,
pf
);
return
0
;
}
/*
* Validate file access.
*/
static
int
validate_access
(
struct
testcase
*
test
,
const
char
*
filename
,
int
mode
)
{
char
*
ptr
;
long
testno
;
logmsg
(
"trying to get file: %s mode %x"
,
filename
,
mode
);
if
(
!
strncmp
(
"/verifiedserver"
,
filename
,
15
))
{
char
weare
[
128
];
size_t
count
=
sprintf
(
weare
,
"WE ROOLZ: %d
\r\n
"
,
(
int
)
getpid
());
logmsg
(
"Are-we-friendly question received"
);
test
->
buffer
=
strdup
(
weare
);
test
->
rptr
=
test
->
buffer
;
/* set read pointer */
test
->
bufsize
=
count
;
/* set total count */
test
->
rcount
=
count
;
/* set data left to read */
return
0
;
/* fine */
}
/* find the last slash */
ptr
=
strrchr
(
filename
,
'/'
);
if
(
ptr
)
{
char
*
file
;
ptr
++
;
/* skip the slash */
/* skip all non-numericals following the slash */
while
(
*
ptr
&&
!
isdigit
((
int
)
*
ptr
))
ptr
++
;
/* get the number */
testno
=
strtol
(
ptr
,
&
ptr
,
10
);
logmsg
(
"requested test number %d"
,
testno
);
test
->
num
=
testno
;
file
=
test2file
(
testno
);
if
(
file
)
{
FILE
*
stream
=
fopen
(
file
,
"rb"
);
if
(
!
stream
)
{
logmsg
(
"Couldn't open test file: %s"
,
file
);
return
EACCESS
;
}
else
{
size_t
count
;
test
->
buffer
=
(
char
*
)
spitout
(
stream
,
"reply"
,
"data"
,
&
count
);
fclose
(
stream
);
if
(
test
->
buffer
)
{
test
->
rptr
=
test
->
buffer
;
/* set read pointer */
test
->
bufsize
=
count
;
/* set total count */
test
->
rcount
=
count
;
/* set data left to read */
}
else
return
EACCESS
;
}
}
else
return
EACCESS
;
}
else
{
logmsg
(
"no slash found in path"
);
return
EACCESS
;
/* failure */
}
return
0
;
}
int
timeout
;
sigjmp_buf
timeoutbuf
;
static
void
timer
(
int
signum
)
{
(
void
)
signum
;
timeout
+=
rexmtval
;
if
(
timeout
>=
maxtimeout
)
exit
(
1
);
siglongjmp
(
timeoutbuf
,
1
);
}
/*
* Send the requested file.
*/
static
void
sendfile
(
struct
testcase
*
test
,
struct
formats
*
pf
)
{
struct
tftphdr
*
dp
;
struct
tftphdr
*
ap
;
/* ack packet */
u_int16_t
block
=
1
;
int
size
,
n
;
mysignal
(
SIGALRM
,
timer
);
dp
=
r_init
();
ap
=
(
struct
tftphdr
*
)
ackbuf
;
do
{
size
=
readit
(
test
,
&
dp
,
pf
->
f_convert
);
if
(
size
<
0
)
{
nak
(
errno
+
100
);
goto
abort
;
}
dp
->
th_opcode
=
htons
((
u_short
)
DATA
);
dp
->
th_block
=
htons
((
u_short
)
block
);
timeout
=
0
;
(
void
)
sigsetjmp
(
timeoutbuf
,
1
);
send_data:
if
(
send
(
peer
,
dp
,
size
+
4
,
0
)
!=
size
+
4
)
{
logmsg
(
"write
\n
"
);
goto
abort
;
}
read_ahead
(
test
,
pf
->
f_convert
);
for
(
;
;
)
{
alarm
(
rexmtval
);
/* read the ack */
n
=
recv
(
peer
,
ackbuf
,
sizeof
(
ackbuf
),
0
);
alarm
(
0
);
if
(
n
<
0
)
{
logmsg
(
"read: fail
\n
"
);
goto
abort
;
}
ap
->
th_opcode
=
ntohs
((
u_short
)
ap
->
th_opcode
);
ap
->
th_block
=
ntohs
((
u_short
)
ap
->
th_block
);
if
(
ap
->
th_opcode
==
ERROR
)
goto
abort
;
if
(
ap
->
th_opcode
==
ACK
)
{
if
(
ap
->
th_block
==
block
)
{
break
;
}
/* Re-synchronize with the other side */
(
void
)
synchnet
(
peer
);
if
(
ap
->
th_block
==
(
block
-
1
))
{
goto
send_data
;
}
}
}
block
++
;
}
while
(
size
==
SEGSIZE
);
abort:
;
}
static
void
justquit
(
int
signum
)
{
(
void
)
signum
;
exit
(
0
);
}
/*
* Receive a file.
*/
static
void
recvfile
(
struct
testcase
*
test
,
struct
formats
*
pf
)
{
struct
tftphdr
*
dp
;
struct
tftphdr
*
ap
;
/* ack buffer */
u_int16_t
block
=
0
;
int
n
,
size
;
mysignal
(
SIGALRM
,
timer
);
dp
=
w_init
();
ap
=
(
struct
tftphdr
*
)
ackbuf
;
do
{
timeout
=
0
;
ap
->
th_opcode
=
htons
((
u_short
)
ACK
);
ap
->
th_block
=
htons
((
u_short
)
block
);
block
++
;
(
void
)
sigsetjmp
(
timeoutbuf
,
1
);
send_ack:
if
(
send
(
peer
,
ackbuf
,
4
,
0
)
!=
4
)
{
logmsg
(
"write: fail
\n
"
);
goto
abort
;
}
write_behind
(
test
,
pf
->
f_convert
);
for
(
;
;
)
{
alarm
(
rexmtval
);
n
=
recv
(
peer
,
dp
,
PKTSIZE
,
0
);
alarm
(
0
);
if
(
n
<
0
)
{
/* really? */
logmsg
(
"read: fail
\n
"
);
goto
abort
;
}
dp
->
th_opcode
=
ntohs
((
u_short
)
dp
->
th_opcode
);
dp
->
th_block
=
ntohs
((
u_short
)
dp
->
th_block
);
if
(
dp
->
th_opcode
==
ERROR
)
goto
abort
;
if
(
dp
->
th_opcode
==
DATA
)
{
if
(
dp
->
th_block
==
block
)
{
break
;
/* normal */
}
/* Re-synchronize with the other side */
(
void
)
synchnet
(
peer
);
if
(
dp
->
th_block
==
(
block
-
1
))
goto
send_ack
;
/* rexmit */
}
}
size
=
writeit
(
test
,
&
dp
,
n
-
4
,
pf
->
f_convert
);
if
(
size
!=
(
n
-
4
))
{
/* ahem */
if
(
size
<
0
)
nak
(
errno
+
100
);
else
nak
(
ENOSPACE
);
goto
abort
;
}
}
while
(
size
==
SEGSIZE
);
write_behind
(
test
,
pf
->
f_convert
);
ap
->
th_opcode
=
htons
((
u_short
)
ACK
);
/* send the "final" ack */
ap
->
th_block
=
htons
((
u_short
)(
block
));
(
void
)
send
(
peer
,
ackbuf
,
4
,
0
);
mysignal
(
SIGALRM
,
justquit
);
/* just quit on timeout */
alarm
(
rexmtval
);
n
=
recv
(
peer
,
buf
,
sizeof
(
buf
),
0
);
/* normally times out and quits */
alarm
(
0
);
if
(
n
>=
4
&&
/* if read some data */
dp
->
th_opcode
==
DATA
&&
/* and got a data block */
block
==
dp
->
th_block
)
{
/* then my last ack was lost */
(
void
)
send
(
peer
,
ackbuf
,
4
,
0
);
/* resend final ack */
}
abort:
return
;
}
struct
errmsg
{
int
e_code
;
const
char
*
e_msg
;
}
errmsgs
[]
=
{
{
EUNDEF
,
"Undefined error code"
},
{
ENOTFOUND
,
"File not found"
},
{
EACCESS
,
"Access violation"
},
{
ENOSPACE
,
"Disk full or allocation exceeded"
},
{
EBADOP
,
"Illegal TFTP operation"
},
{
EBADID
,
"Unknown transfer ID"
},
{
EEXISTS
,
"File already exists"
},
{
ENOUSER
,
"No such user"
},
{
-
1
,
0
}
};
/*
* Send a nak packet (error message). Error code passed in is one of the
* standard TFTP codes, or a UNIX errno offset by 100.
*/
static
void
nak
(
int
error
)
{
struct
tftphdr
*
tp
;
int
length
;
struct
errmsg
*
pe
;
tp
=
(
struct
tftphdr
*
)
buf
;
tp
->
th_opcode
=
htons
((
u_short
)
ERROR
);
tp
->
th_code
=
htons
((
u_short
)
error
);
for
(
pe
=
errmsgs
;
pe
->
e_code
>=
0
;
pe
++
)
if
(
pe
->
e_code
==
error
)
break
;
if
(
pe
->
e_code
<
0
)
{
pe
->
e_msg
=
strerror
(
error
-
100
);
tp
->
th_code
=
EUNDEF
;
/* set 'undef' errorcode */
}
strcpy
(
tp
->
th_msg
,
pe
->
e_msg
);
length
=
strlen
(
pe
->
e_msg
);
tp
->
th_msg
[
length
]
=
'\0'
;
length
+=
5
;
if
(
send
(
peer
,
buf
,
length
,
0
)
!=
length
)
logmsg
(
"nak: fail
\n
"
);
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment