113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci *
913498266Sopenharmony_ci * Trivial file transfer protocol server.
1013498266Sopenharmony_ci *
1113498266Sopenharmony_ci * This code includes many modifications by Jim Guyton <guyton@rand-unix>
1213498266Sopenharmony_ci *
1313498266Sopenharmony_ci * This source file was started based on netkit-tftpd 0.17
1413498266Sopenharmony_ci * Heavily modified for curl's test suite
1513498266Sopenharmony_ci */
1613498266Sopenharmony_ci
1713498266Sopenharmony_ci/*
1813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
1913498266Sopenharmony_ci * Copyright (c) 1983, Regents of the University of California.
2013498266Sopenharmony_ci * All rights reserved.
2113498266Sopenharmony_ci *
2213498266Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
2313498266Sopenharmony_ci * modification, are permitted provided that the following conditions
2413498266Sopenharmony_ci * are met:
2513498266Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
2613498266Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
2713498266Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
2813498266Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
2913498266Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
3013498266Sopenharmony_ci * 3. All advertising materials mentioning features or use of this software
3113498266Sopenharmony_ci *    must display the following acknowledgement:
3213498266Sopenharmony_ci *      This product includes software developed by the University of
3313498266Sopenharmony_ci *      California, Berkeley and its contributors.
3413498266Sopenharmony_ci * 4. Neither the name of the University nor the names of its contributors
3513498266Sopenharmony_ci *    may be used to endorse or promote products derived from this software
3613498266Sopenharmony_ci *    without specific prior written permission.
3713498266Sopenharmony_ci *
3813498266Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
3913498266Sopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4013498266Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4113498266Sopenharmony_ci * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
4213498266Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4313498266Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4413498266Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4513498266Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4613498266Sopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
4713498266Sopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4813498266Sopenharmony_ci * SUCH DAMAGE.
4913498266Sopenharmony_ci *
5013498266Sopenharmony_ci * SPDX-License-Identifier: BSD-4-Clause-UC
5113498266Sopenharmony_ci */
5213498266Sopenharmony_ci
5313498266Sopenharmony_ci#include "server_setup.h"
5413498266Sopenharmony_ci
5513498266Sopenharmony_ci#ifdef HAVE_SYS_IOCTL_H
5613498266Sopenharmony_ci#include <sys/ioctl.h>
5713498266Sopenharmony_ci#endif
5813498266Sopenharmony_ci#include <signal.h>
5913498266Sopenharmony_ci#ifdef HAVE_FCNTL_H
6013498266Sopenharmony_ci#include <fcntl.h>
6113498266Sopenharmony_ci#endif
6213498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
6313498266Sopenharmony_ci#include <netinet/in.h>
6413498266Sopenharmony_ci#endif
6513498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H
6613498266Sopenharmony_ci#include <arpa/inet.h>
6713498266Sopenharmony_ci#endif
6813498266Sopenharmony_ci#ifdef HAVE_NETDB_H
6913498266Sopenharmony_ci#include <netdb.h>
7013498266Sopenharmony_ci#endif
7113498266Sopenharmony_ci#ifdef HAVE_SYS_FILIO_H
7213498266Sopenharmony_ci/* FIONREAD on Solaris 7 */
7313498266Sopenharmony_ci#include <sys/filio.h>
7413498266Sopenharmony_ci#endif
7513498266Sopenharmony_ci
7613498266Sopenharmony_ci#include <setjmp.h>
7713498266Sopenharmony_ci
7813498266Sopenharmony_ci#ifdef HAVE_PWD_H
7913498266Sopenharmony_ci#include <pwd.h>
8013498266Sopenharmony_ci#endif
8113498266Sopenharmony_ci
8213498266Sopenharmony_ci#include <ctype.h>
8313498266Sopenharmony_ci
8413498266Sopenharmony_ci#define ENABLE_CURLX_PRINTF
8513498266Sopenharmony_ci/* make the curlx header define all printf() functions to use the curlx_*
8613498266Sopenharmony_ci   versions instead */
8713498266Sopenharmony_ci#include "curlx.h" /* from the private lib dir */
8813498266Sopenharmony_ci#include "getpart.h"
8913498266Sopenharmony_ci#include "util.h"
9013498266Sopenharmony_ci#include "server_sockaddr.h"
9113498266Sopenharmony_ci#include "tftp.h"
9213498266Sopenharmony_ci
9313498266Sopenharmony_ci/* include memdebug.h last */
9413498266Sopenharmony_ci#include "memdebug.h"
9513498266Sopenharmony_ci
9613498266Sopenharmony_ci/*****************************************************************************
9713498266Sopenharmony_ci*                      STRUCT DECLARATIONS AND DEFINES                       *
9813498266Sopenharmony_ci*****************************************************************************/
9913498266Sopenharmony_ci
10013498266Sopenharmony_ci#ifndef PKTSIZE
10113498266Sopenharmony_ci#define PKTSIZE (SEGSIZE + 4)  /* SEGSIZE defined in arpa/tftp.h */
10213498266Sopenharmony_ci#endif
10313498266Sopenharmony_ci
10413498266Sopenharmony_cistruct testcase {
10513498266Sopenharmony_ci  char *buffer;   /* holds the file data to send to the client */
10613498266Sopenharmony_ci  size_t bufsize; /* size of the data in buffer */
10713498266Sopenharmony_ci  char *rptr;     /* read pointer into the buffer */
10813498266Sopenharmony_ci  size_t rcount;  /* amount of data left to read of the file */
10913498266Sopenharmony_ci  long testno;    /* test case number */
11013498266Sopenharmony_ci  int ofile;      /* file descriptor for output file when uploading to us */
11113498266Sopenharmony_ci
11213498266Sopenharmony_ci  int writedelay; /* number of seconds between each packet */
11313498266Sopenharmony_ci};
11413498266Sopenharmony_ci
11513498266Sopenharmony_cistruct formats {
11613498266Sopenharmony_ci  const char *f_mode;
11713498266Sopenharmony_ci  int f_convert;
11813498266Sopenharmony_ci};
11913498266Sopenharmony_ci
12013498266Sopenharmony_cistruct errmsg {
12113498266Sopenharmony_ci  int e_code;
12213498266Sopenharmony_ci  const char *e_msg;
12313498266Sopenharmony_ci};
12413498266Sopenharmony_ci
12513498266Sopenharmony_citypedef union {
12613498266Sopenharmony_ci  struct tftphdr hdr;
12713498266Sopenharmony_ci  char storage[PKTSIZE];
12813498266Sopenharmony_ci} tftphdr_storage_t;
12913498266Sopenharmony_ci
13013498266Sopenharmony_ci/*
13113498266Sopenharmony_ci * bf.counter values in range [-1 .. SEGSIZE] represents size of data in the
13213498266Sopenharmony_ci * bf.buf buffer. Additionally it can also hold flags BF_ALLOC or BF_FREE.
13313498266Sopenharmony_ci */
13413498266Sopenharmony_ci
13513498266Sopenharmony_cistruct bf {
13613498266Sopenharmony_ci  int counter;            /* size of data in buffer, or flag */
13713498266Sopenharmony_ci  tftphdr_storage_t buf;  /* room for data packet */
13813498266Sopenharmony_ci};
13913498266Sopenharmony_ci
14013498266Sopenharmony_ci#define BF_ALLOC -3       /* alloc'd but not yet filled */
14113498266Sopenharmony_ci#define BF_FREE  -2       /* free */
14213498266Sopenharmony_ci
14313498266Sopenharmony_ci#define opcode_RRQ   1
14413498266Sopenharmony_ci#define opcode_WRQ   2
14513498266Sopenharmony_ci#define opcode_DATA  3
14613498266Sopenharmony_ci#define opcode_ACK   4
14713498266Sopenharmony_ci#define opcode_ERROR 5
14813498266Sopenharmony_ci
14913498266Sopenharmony_ci#define TIMEOUT      5
15013498266Sopenharmony_ci
15113498266Sopenharmony_ci#undef MIN
15213498266Sopenharmony_ci#define MIN(x,y) ((x)<(y)?(x):(y))
15313498266Sopenharmony_ci
15413498266Sopenharmony_ci#ifndef DEFAULT_LOGFILE
15513498266Sopenharmony_ci#define DEFAULT_LOGFILE "log/tftpd.log"
15613498266Sopenharmony_ci#endif
15713498266Sopenharmony_ci
15813498266Sopenharmony_ci#define REQUEST_DUMP  "server.input"
15913498266Sopenharmony_ci
16013498266Sopenharmony_ci#define DEFAULT_PORT 8999 /* UDP */
16113498266Sopenharmony_ci
16213498266Sopenharmony_ci/*****************************************************************************
16313498266Sopenharmony_ci*                              GLOBAL VARIABLES                              *
16413498266Sopenharmony_ci*****************************************************************************/
16513498266Sopenharmony_ci
16613498266Sopenharmony_cistatic struct errmsg errmsgs[] = {
16713498266Sopenharmony_ci  { EUNDEF,       "Undefined error code" },
16813498266Sopenharmony_ci  { ENOTFOUND,    "File not found" },
16913498266Sopenharmony_ci  { EACCESS,      "Access violation" },
17013498266Sopenharmony_ci  { ENOSPACE,     "Disk full or allocation exceeded" },
17113498266Sopenharmony_ci  { EBADOP,       "Illegal TFTP operation" },
17213498266Sopenharmony_ci  { EBADID,       "Unknown transfer ID" },
17313498266Sopenharmony_ci  { EEXISTS,      "File already exists" },
17413498266Sopenharmony_ci  { ENOUSER,      "No such user" },
17513498266Sopenharmony_ci  { -1,           0 }
17613498266Sopenharmony_ci};
17713498266Sopenharmony_ci
17813498266Sopenharmony_cistatic const struct formats formata[] = {
17913498266Sopenharmony_ci  { "netascii",   1 },
18013498266Sopenharmony_ci  { "octet",      0 },
18113498266Sopenharmony_ci  { NULL,         0 }
18213498266Sopenharmony_ci};
18313498266Sopenharmony_ci
18413498266Sopenharmony_cistatic struct bf bfs[2];
18513498266Sopenharmony_ci
18613498266Sopenharmony_cistatic int nextone;     /* index of next buffer to use */
18713498266Sopenharmony_cistatic int current;     /* index of buffer in use */
18813498266Sopenharmony_ci
18913498266Sopenharmony_ci                           /* control flags for crlf conversions */
19013498266Sopenharmony_cistatic int newline = 0;    /* fillbuf: in middle of newline expansion */
19113498266Sopenharmony_cistatic int prevchar = -1;  /* putbuf: previous char (cr check) */
19213498266Sopenharmony_ci
19313498266Sopenharmony_cistatic tftphdr_storage_t buf;
19413498266Sopenharmony_cistatic tftphdr_storage_t ackbuf;
19513498266Sopenharmony_ci
19613498266Sopenharmony_cistatic srvr_sockaddr_union_t from;
19713498266Sopenharmony_cistatic curl_socklen_t fromlen;
19813498266Sopenharmony_ci
19913498266Sopenharmony_cistatic curl_socket_t peer = CURL_SOCKET_BAD;
20013498266Sopenharmony_ci
20113498266Sopenharmony_cistatic unsigned int timeout;
20213498266Sopenharmony_cistatic unsigned int maxtimeout = 5 * TIMEOUT;
20313498266Sopenharmony_ci
20413498266Sopenharmony_ci#ifdef ENABLE_IPV6
20513498266Sopenharmony_cistatic bool use_ipv6 = FALSE;
20613498266Sopenharmony_ci#endif
20713498266Sopenharmony_cistatic const char *ipv_inuse = "IPv4";
20813498266Sopenharmony_ci
20913498266Sopenharmony_ciconst char *serverlogfile = DEFAULT_LOGFILE;
21013498266Sopenharmony_cistatic const char *logdir = "log";
21113498266Sopenharmony_cistatic char loglockfile[256];
21213498266Sopenharmony_cistatic const char *pidname = ".tftpd.pid";
21313498266Sopenharmony_cistatic const char *portname = NULL; /* none by default */
21413498266Sopenharmony_cistatic int serverlogslocked = 0;
21513498266Sopenharmony_cistatic int wrotepidfile = 0;
21613498266Sopenharmony_cistatic int wroteportfile = 0;
21713498266Sopenharmony_ci
21813498266Sopenharmony_ci#ifdef HAVE_SIGSETJMP
21913498266Sopenharmony_cistatic sigjmp_buf timeoutbuf;
22013498266Sopenharmony_ci#endif
22113498266Sopenharmony_ci
22213498266Sopenharmony_ci#if defined(HAVE_ALARM) && defined(SIGALRM)
22313498266Sopenharmony_cistatic const unsigned int rexmtval = TIMEOUT;
22413498266Sopenharmony_ci#endif
22513498266Sopenharmony_ci
22613498266Sopenharmony_ci/*****************************************************************************
22713498266Sopenharmony_ci*                            FUNCTION PROTOTYPES                             *
22813498266Sopenharmony_ci*****************************************************************************/
22913498266Sopenharmony_ci
23013498266Sopenharmony_cistatic struct tftphdr *rw_init(int);
23113498266Sopenharmony_ci
23213498266Sopenharmony_cistatic struct tftphdr *w_init(void);
23313498266Sopenharmony_ci
23413498266Sopenharmony_cistatic struct tftphdr *r_init(void);
23513498266Sopenharmony_ci
23613498266Sopenharmony_cistatic void read_ahead(struct testcase *test, int convert);
23713498266Sopenharmony_ci
23813498266Sopenharmony_cistatic ssize_t write_behind(struct testcase *test, int convert);
23913498266Sopenharmony_ci
24013498266Sopenharmony_cistatic int synchnet(curl_socket_t);
24113498266Sopenharmony_ci
24213498266Sopenharmony_cistatic int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size);
24313498266Sopenharmony_ci
24413498266Sopenharmony_cistatic int validate_access(struct testcase *test, const char *fname, int mode);
24513498266Sopenharmony_ci
24613498266Sopenharmony_cistatic void sendtftp(struct testcase *test, const struct formats *pf);
24713498266Sopenharmony_ci
24813498266Sopenharmony_cistatic void recvtftp(struct testcase *test, const struct formats *pf);
24913498266Sopenharmony_ci
25013498266Sopenharmony_cistatic void nak(int error);
25113498266Sopenharmony_ci
25213498266Sopenharmony_ci#if defined(HAVE_ALARM) && defined(SIGALRM)
25313498266Sopenharmony_ci
25413498266Sopenharmony_cistatic void mysignal(int sig, void (*handler)(int));
25513498266Sopenharmony_ci
25613498266Sopenharmony_cistatic void timer(int signum);
25713498266Sopenharmony_ci
25813498266Sopenharmony_cistatic void justtimeout(int signum);
25913498266Sopenharmony_ci
26013498266Sopenharmony_ci#endif /* HAVE_ALARM && SIGALRM */
26113498266Sopenharmony_ci
26213498266Sopenharmony_ci/*****************************************************************************
26313498266Sopenharmony_ci*                          FUNCTION IMPLEMENTATIONS                          *
26413498266Sopenharmony_ci*****************************************************************************/
26513498266Sopenharmony_ci
26613498266Sopenharmony_ci#if defined(HAVE_ALARM) && defined(SIGALRM)
26713498266Sopenharmony_ci
26813498266Sopenharmony_ci/*
26913498266Sopenharmony_ci * Like signal(), but with well-defined semantics.
27013498266Sopenharmony_ci */
27113498266Sopenharmony_cistatic void mysignal(int sig, void (*handler)(int))
27213498266Sopenharmony_ci{
27313498266Sopenharmony_ci  struct sigaction sa;
27413498266Sopenharmony_ci  memset(&sa, 0, sizeof(sa));
27513498266Sopenharmony_ci  sa.sa_handler = handler;
27613498266Sopenharmony_ci  sigaction(sig, &sa, NULL);
27713498266Sopenharmony_ci}
27813498266Sopenharmony_ci
27913498266Sopenharmony_cistatic void timer(int signum)
28013498266Sopenharmony_ci{
28113498266Sopenharmony_ci  (void)signum;
28213498266Sopenharmony_ci
28313498266Sopenharmony_ci  logmsg("alarm!");
28413498266Sopenharmony_ci
28513498266Sopenharmony_ci  timeout += rexmtval;
28613498266Sopenharmony_ci  if(timeout >= maxtimeout) {
28713498266Sopenharmony_ci    if(wrotepidfile) {
28813498266Sopenharmony_ci      wrotepidfile = 0;
28913498266Sopenharmony_ci      unlink(pidname);
29013498266Sopenharmony_ci    }
29113498266Sopenharmony_ci    if(wroteportfile) {
29213498266Sopenharmony_ci      wroteportfile = 0;
29313498266Sopenharmony_ci      unlink(portname);
29413498266Sopenharmony_ci    }
29513498266Sopenharmony_ci    if(serverlogslocked) {
29613498266Sopenharmony_ci      serverlogslocked = 0;
29713498266Sopenharmony_ci      clear_advisor_read_lock(loglockfile);
29813498266Sopenharmony_ci    }
29913498266Sopenharmony_ci    exit(1);
30013498266Sopenharmony_ci  }
30113498266Sopenharmony_ci#ifdef HAVE_SIGSETJMP
30213498266Sopenharmony_ci  siglongjmp(timeoutbuf, 1);
30313498266Sopenharmony_ci#endif
30413498266Sopenharmony_ci}
30513498266Sopenharmony_ci
30613498266Sopenharmony_cistatic void justtimeout(int signum)
30713498266Sopenharmony_ci{
30813498266Sopenharmony_ci  (void)signum;
30913498266Sopenharmony_ci}
31013498266Sopenharmony_ci
31113498266Sopenharmony_ci#endif /* HAVE_ALARM && SIGALRM */
31213498266Sopenharmony_ci
31313498266Sopenharmony_ci/*
31413498266Sopenharmony_ci * init for either read-ahead or write-behind.
31513498266Sopenharmony_ci * zero for write-behind, one for read-head.
31613498266Sopenharmony_ci */
31713498266Sopenharmony_cistatic struct tftphdr *rw_init(int x)
31813498266Sopenharmony_ci{
31913498266Sopenharmony_ci  newline = 0;                    /* init crlf flag */
32013498266Sopenharmony_ci  prevchar = -1;
32113498266Sopenharmony_ci  bfs[0].counter =  BF_ALLOC;     /* pass out the first buffer */
32213498266Sopenharmony_ci  current = 0;
32313498266Sopenharmony_ci  bfs[1].counter = BF_FREE;
32413498266Sopenharmony_ci  nextone = x;                    /* ahead or behind? */
32513498266Sopenharmony_ci  return &bfs[0].buf.hdr;
32613498266Sopenharmony_ci}
32713498266Sopenharmony_ci
32813498266Sopenharmony_cistatic struct tftphdr *w_init(void)
32913498266Sopenharmony_ci{
33013498266Sopenharmony_ci  return rw_init(0); /* write-behind */
33113498266Sopenharmony_ci}
33213498266Sopenharmony_ci
33313498266Sopenharmony_cistatic struct tftphdr *r_init(void)
33413498266Sopenharmony_ci{
33513498266Sopenharmony_ci  return rw_init(1); /* read-ahead */
33613498266Sopenharmony_ci}
33713498266Sopenharmony_ci
33813498266Sopenharmony_ci/* Have emptied current buffer by sending to net and getting ack.
33913498266Sopenharmony_ci   Free it and return next buffer filled with data.
34013498266Sopenharmony_ci */
34113498266Sopenharmony_cistatic int readit(struct testcase *test, struct tftphdr **dpp,
34213498266Sopenharmony_ci                  int convert /* if true, convert to ascii */)
34313498266Sopenharmony_ci{
34413498266Sopenharmony_ci  struct bf *b;
34513498266Sopenharmony_ci
34613498266Sopenharmony_ci  bfs[current].counter = BF_FREE; /* free old one */
34713498266Sopenharmony_ci  current = !current;             /* "incr" current */
34813498266Sopenharmony_ci
34913498266Sopenharmony_ci  b = &bfs[current];              /* look at new buffer */
35013498266Sopenharmony_ci  if(b->counter == BF_FREE)      /* if it's empty */
35113498266Sopenharmony_ci    read_ahead(test, convert);    /* fill it */
35213498266Sopenharmony_ci
35313498266Sopenharmony_ci  *dpp = &b->buf.hdr;             /* set caller's ptr */
35413498266Sopenharmony_ci  return b->counter;
35513498266Sopenharmony_ci}
35613498266Sopenharmony_ci
35713498266Sopenharmony_ci/*
35813498266Sopenharmony_ci * fill the input buffer, doing ascii conversions if requested
35913498266Sopenharmony_ci * conversions are  lf -> cr, lf  and cr -> cr, nul
36013498266Sopenharmony_ci */
36113498266Sopenharmony_cistatic void read_ahead(struct testcase *test,
36213498266Sopenharmony_ci                       int convert /* if true, convert to ascii */)
36313498266Sopenharmony_ci{
36413498266Sopenharmony_ci  int i;
36513498266Sopenharmony_ci  char *p;
36613498266Sopenharmony_ci  int c;
36713498266Sopenharmony_ci  struct bf *b;
36813498266Sopenharmony_ci  struct tftphdr *dp;
36913498266Sopenharmony_ci
37013498266Sopenharmony_ci  b = &bfs[nextone];              /* look at "next" buffer */
37113498266Sopenharmony_ci  if(b->counter != BF_FREE)      /* nop if not free */
37213498266Sopenharmony_ci    return;
37313498266Sopenharmony_ci  nextone = !nextone;             /* "incr" next buffer ptr */
37413498266Sopenharmony_ci
37513498266Sopenharmony_ci  dp = &b->buf.hdr;
37613498266Sopenharmony_ci
37713498266Sopenharmony_ci  if(convert == 0) {
37813498266Sopenharmony_ci    /* The former file reading code did this:
37913498266Sopenharmony_ci       b->counter = read(fileno(file), dp->th_data, SEGSIZE); */
38013498266Sopenharmony_ci    size_t copy_n = MIN(SEGSIZE, test->rcount);
38113498266Sopenharmony_ci    memcpy(dp->th_data, test->rptr, copy_n);
38213498266Sopenharmony_ci
38313498266Sopenharmony_ci    /* decrease amount, advance pointer */
38413498266Sopenharmony_ci    test->rcount -= copy_n;
38513498266Sopenharmony_ci    test->rptr += copy_n;
38613498266Sopenharmony_ci    b->counter = (int)copy_n;
38713498266Sopenharmony_ci    return;
38813498266Sopenharmony_ci  }
38913498266Sopenharmony_ci
39013498266Sopenharmony_ci  p = dp->th_data;
39113498266Sopenharmony_ci  for(i = 0 ; i < SEGSIZE; i++) {
39213498266Sopenharmony_ci    if(newline) {
39313498266Sopenharmony_ci      if(prevchar == '\n')
39413498266Sopenharmony_ci        c = '\n';       /* lf to cr,lf */
39513498266Sopenharmony_ci      else
39613498266Sopenharmony_ci        c = '\0';       /* cr to cr,nul */
39713498266Sopenharmony_ci      newline = 0;
39813498266Sopenharmony_ci    }
39913498266Sopenharmony_ci    else {
40013498266Sopenharmony_ci      if(test->rcount) {
40113498266Sopenharmony_ci        c = test->rptr[0];
40213498266Sopenharmony_ci        test->rptr++;
40313498266Sopenharmony_ci        test->rcount--;
40413498266Sopenharmony_ci      }
40513498266Sopenharmony_ci      else
40613498266Sopenharmony_ci        break;
40713498266Sopenharmony_ci      if(c == '\n' || c == '\r') {
40813498266Sopenharmony_ci        prevchar = c;
40913498266Sopenharmony_ci        c = '\r';
41013498266Sopenharmony_ci        newline = 1;
41113498266Sopenharmony_ci      }
41213498266Sopenharmony_ci    }
41313498266Sopenharmony_ci    *p++ = (char)c;
41413498266Sopenharmony_ci  }
41513498266Sopenharmony_ci  b->counter = (int)(p - dp->th_data);
41613498266Sopenharmony_ci}
41713498266Sopenharmony_ci
41813498266Sopenharmony_ci/* Update count associated with the buffer, get new buffer from the queue.
41913498266Sopenharmony_ci   Calls write_behind only if next buffer not available.
42013498266Sopenharmony_ci */
42113498266Sopenharmony_cistatic int writeit(struct testcase *test, struct tftphdr * volatile *dpp,
42213498266Sopenharmony_ci                   int ct, int convert)
42313498266Sopenharmony_ci{
42413498266Sopenharmony_ci  bfs[current].counter = ct;      /* set size of data to write */
42513498266Sopenharmony_ci  current = !current;             /* switch to other buffer */
42613498266Sopenharmony_ci  if(bfs[current].counter != BF_FREE)     /* if not free */
42713498266Sopenharmony_ci    write_behind(test, convert);     /* flush it */
42813498266Sopenharmony_ci  bfs[current].counter = BF_ALLOC;        /* mark as alloc'd */
42913498266Sopenharmony_ci  *dpp =  &bfs[current].buf.hdr;
43013498266Sopenharmony_ci  return ct;                      /* this is a lie of course */
43113498266Sopenharmony_ci}
43213498266Sopenharmony_ci
43313498266Sopenharmony_ci/*
43413498266Sopenharmony_ci * Output a buffer to a file, converting from netascii if requested.
43513498266Sopenharmony_ci * CR, NUL -> CR  and CR, LF => LF.
43613498266Sopenharmony_ci * Note spec is undefined if we get CR as last byte of file or a
43713498266Sopenharmony_ci * CR followed by anything else.  In this case we leave it alone.
43813498266Sopenharmony_ci */
43913498266Sopenharmony_cistatic ssize_t write_behind(struct testcase *test, int convert)
44013498266Sopenharmony_ci{
44113498266Sopenharmony_ci  char *writebuf;
44213498266Sopenharmony_ci  int count;
44313498266Sopenharmony_ci  int ct;
44413498266Sopenharmony_ci  char *p;
44513498266Sopenharmony_ci  int c;                          /* current character */
44613498266Sopenharmony_ci  struct bf *b;
44713498266Sopenharmony_ci  struct tftphdr *dp;
44813498266Sopenharmony_ci
44913498266Sopenharmony_ci  b = &bfs[nextone];
45013498266Sopenharmony_ci  if(b->counter < -1)            /* anything to flush? */
45113498266Sopenharmony_ci    return 0;                     /* just nop if nothing to do */
45213498266Sopenharmony_ci
45313498266Sopenharmony_ci  if(!test->ofile) {
45413498266Sopenharmony_ci    char outfile[256];
45513498266Sopenharmony_ci    msnprintf(outfile, sizeof(outfile), "%s/upload.%ld", logdir, test->testno);
45613498266Sopenharmony_ci#ifdef _WIN32
45713498266Sopenharmony_ci    test->ofile = open(outfile, O_CREAT|O_RDWR|O_BINARY, 0777);
45813498266Sopenharmony_ci#else
45913498266Sopenharmony_ci    test->ofile = open(outfile, O_CREAT|O_RDWR, 0777);
46013498266Sopenharmony_ci#endif
46113498266Sopenharmony_ci    if(test->ofile == -1) {
46213498266Sopenharmony_ci      logmsg("Couldn't create and/or open file %s for upload!", outfile);
46313498266Sopenharmony_ci      return -1; /* failure! */
46413498266Sopenharmony_ci    }
46513498266Sopenharmony_ci  }
46613498266Sopenharmony_ci
46713498266Sopenharmony_ci  count = b->counter;             /* remember byte count */
46813498266Sopenharmony_ci  b->counter = BF_FREE;           /* reset flag */
46913498266Sopenharmony_ci  dp = &b->buf.hdr;
47013498266Sopenharmony_ci  nextone = !nextone;             /* incr for next time */
47113498266Sopenharmony_ci  writebuf = dp->th_data;
47213498266Sopenharmony_ci
47313498266Sopenharmony_ci  if(count <= 0)
47413498266Sopenharmony_ci    return -1;                    /* nak logic? */
47513498266Sopenharmony_ci
47613498266Sopenharmony_ci  if(convert == 0)
47713498266Sopenharmony_ci    return write(test->ofile, writebuf, count);
47813498266Sopenharmony_ci
47913498266Sopenharmony_ci  p = writebuf;
48013498266Sopenharmony_ci  ct = count;
48113498266Sopenharmony_ci  while(ct--) {                   /* loop over the buffer */
48213498266Sopenharmony_ci    c = *p++;                     /* pick up a character */
48313498266Sopenharmony_ci    if(prevchar == '\r') {        /* if prev char was cr */
48413498266Sopenharmony_ci      if(c == '\n')               /* if have cr,lf then just */
48513498266Sopenharmony_ci        lseek(test->ofile, -1, SEEK_CUR); /* smash lf on top of the cr */
48613498266Sopenharmony_ci      else
48713498266Sopenharmony_ci        if(c == '\0')             /* if have cr,nul then */
48813498266Sopenharmony_ci          goto skipit;            /* just skip over the putc */
48913498266Sopenharmony_ci      /* else just fall through and allow it */
49013498266Sopenharmony_ci    }
49113498266Sopenharmony_ci    /* formerly
49213498266Sopenharmony_ci       putc(c, file); */
49313498266Sopenharmony_ci    if(1 != write(test->ofile, &c, 1))
49413498266Sopenharmony_ci      break;
49513498266Sopenharmony_ciskipit:
49613498266Sopenharmony_ci    prevchar = c;
49713498266Sopenharmony_ci  }
49813498266Sopenharmony_ci  return count;
49913498266Sopenharmony_ci}
50013498266Sopenharmony_ci
50113498266Sopenharmony_ci/* When an error has occurred, it is possible that the two sides are out of
50213498266Sopenharmony_ci * synch.  Ie: that what I think is the other side's response to packet N is
50313498266Sopenharmony_ci * really their response to packet N-1.
50413498266Sopenharmony_ci *
50513498266Sopenharmony_ci * So, to try to prevent that, we flush all the input queued up for us on the
50613498266Sopenharmony_ci * network connection on our host.
50713498266Sopenharmony_ci *
50813498266Sopenharmony_ci * We return the number of packets we flushed (mostly for reporting when trace
50913498266Sopenharmony_ci * is active).
51013498266Sopenharmony_ci */
51113498266Sopenharmony_ci
51213498266Sopenharmony_cistatic int synchnet(curl_socket_t f /* socket to flush */)
51313498266Sopenharmony_ci{
51413498266Sopenharmony_ci
51513498266Sopenharmony_ci#if defined(HAVE_IOCTLSOCKET)
51613498266Sopenharmony_ci  unsigned long i;
51713498266Sopenharmony_ci#else
51813498266Sopenharmony_ci  int i;
51913498266Sopenharmony_ci#endif
52013498266Sopenharmony_ci  int j = 0;
52113498266Sopenharmony_ci  char rbuf[PKTSIZE];
52213498266Sopenharmony_ci  srvr_sockaddr_union_t fromaddr;
52313498266Sopenharmony_ci  curl_socklen_t fromaddrlen;
52413498266Sopenharmony_ci
52513498266Sopenharmony_ci  for(;;) {
52613498266Sopenharmony_ci#if defined(HAVE_IOCTLSOCKET)
52713498266Sopenharmony_ci    (void) ioctlsocket(f, FIONREAD, &i);
52813498266Sopenharmony_ci#else
52913498266Sopenharmony_ci    (void) ioctl(f, FIONREAD, &i);
53013498266Sopenharmony_ci#endif
53113498266Sopenharmony_ci    if(i) {
53213498266Sopenharmony_ci      j++;
53313498266Sopenharmony_ci#ifdef ENABLE_IPV6
53413498266Sopenharmony_ci      if(!use_ipv6)
53513498266Sopenharmony_ci#endif
53613498266Sopenharmony_ci        fromaddrlen = sizeof(fromaddr.sa4);
53713498266Sopenharmony_ci#ifdef ENABLE_IPV6
53813498266Sopenharmony_ci      else
53913498266Sopenharmony_ci        fromaddrlen = sizeof(fromaddr.sa6);
54013498266Sopenharmony_ci#endif
54113498266Sopenharmony_ci      (void) recvfrom(f, rbuf, sizeof(rbuf), 0,
54213498266Sopenharmony_ci                      &fromaddr.sa, &fromaddrlen);
54313498266Sopenharmony_ci    }
54413498266Sopenharmony_ci    else
54513498266Sopenharmony_ci      break;
54613498266Sopenharmony_ci  }
54713498266Sopenharmony_ci  return j;
54813498266Sopenharmony_ci}
54913498266Sopenharmony_ci
55013498266Sopenharmony_ciint main(int argc, char **argv)
55113498266Sopenharmony_ci{
55213498266Sopenharmony_ci  srvr_sockaddr_union_t me;
55313498266Sopenharmony_ci  struct tftphdr *tp;
55413498266Sopenharmony_ci  ssize_t n = 0;
55513498266Sopenharmony_ci  int arg = 1;
55613498266Sopenharmony_ci  unsigned short port = DEFAULT_PORT;
55713498266Sopenharmony_ci  curl_socket_t sock = CURL_SOCKET_BAD;
55813498266Sopenharmony_ci  int flag;
55913498266Sopenharmony_ci  int rc;
56013498266Sopenharmony_ci  int error;
56113498266Sopenharmony_ci  struct testcase test;
56213498266Sopenharmony_ci  int result = 0;
56313498266Sopenharmony_ci
56413498266Sopenharmony_ci  memset(&test, 0, sizeof(test));
56513498266Sopenharmony_ci
56613498266Sopenharmony_ci  while(argc>arg) {
56713498266Sopenharmony_ci    if(!strcmp("--version", argv[arg])) {
56813498266Sopenharmony_ci      printf("tftpd IPv4%s\n",
56913498266Sopenharmony_ci#ifdef ENABLE_IPV6
57013498266Sopenharmony_ci             "/IPv6"
57113498266Sopenharmony_ci#else
57213498266Sopenharmony_ci             ""
57313498266Sopenharmony_ci#endif
57413498266Sopenharmony_ci             );
57513498266Sopenharmony_ci      return 0;
57613498266Sopenharmony_ci    }
57713498266Sopenharmony_ci    else if(!strcmp("--pidfile", argv[arg])) {
57813498266Sopenharmony_ci      arg++;
57913498266Sopenharmony_ci      if(argc>arg)
58013498266Sopenharmony_ci        pidname = argv[arg++];
58113498266Sopenharmony_ci    }
58213498266Sopenharmony_ci    else if(!strcmp("--portfile", argv[arg])) {
58313498266Sopenharmony_ci      arg++;
58413498266Sopenharmony_ci      if(argc>arg)
58513498266Sopenharmony_ci        portname = argv[arg++];
58613498266Sopenharmony_ci    }
58713498266Sopenharmony_ci    else if(!strcmp("--logfile", argv[arg])) {
58813498266Sopenharmony_ci      arg++;
58913498266Sopenharmony_ci      if(argc>arg)
59013498266Sopenharmony_ci        serverlogfile = argv[arg++];
59113498266Sopenharmony_ci    }
59213498266Sopenharmony_ci    else if(!strcmp("--logdir", argv[arg])) {
59313498266Sopenharmony_ci      arg++;
59413498266Sopenharmony_ci      if(argc>arg)
59513498266Sopenharmony_ci        logdir = argv[arg++];
59613498266Sopenharmony_ci    }
59713498266Sopenharmony_ci    else if(!strcmp("--ipv4", argv[arg])) {
59813498266Sopenharmony_ci#ifdef ENABLE_IPV6
59913498266Sopenharmony_ci      ipv_inuse = "IPv4";
60013498266Sopenharmony_ci      use_ipv6 = FALSE;
60113498266Sopenharmony_ci#endif
60213498266Sopenharmony_ci      arg++;
60313498266Sopenharmony_ci    }
60413498266Sopenharmony_ci    else if(!strcmp("--ipv6", argv[arg])) {
60513498266Sopenharmony_ci#ifdef ENABLE_IPV6
60613498266Sopenharmony_ci      ipv_inuse = "IPv6";
60713498266Sopenharmony_ci      use_ipv6 = TRUE;
60813498266Sopenharmony_ci#endif
60913498266Sopenharmony_ci      arg++;
61013498266Sopenharmony_ci    }
61113498266Sopenharmony_ci    else if(!strcmp("--port", argv[arg])) {
61213498266Sopenharmony_ci      arg++;
61313498266Sopenharmony_ci      if(argc>arg) {
61413498266Sopenharmony_ci        char *endptr;
61513498266Sopenharmony_ci        unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
61613498266Sopenharmony_ci        port = curlx_ultous(ulnum);
61713498266Sopenharmony_ci        arg++;
61813498266Sopenharmony_ci      }
61913498266Sopenharmony_ci    }
62013498266Sopenharmony_ci    else if(!strcmp("--srcdir", argv[arg])) {
62113498266Sopenharmony_ci      arg++;
62213498266Sopenharmony_ci      if(argc>arg) {
62313498266Sopenharmony_ci        path = argv[arg];
62413498266Sopenharmony_ci        arg++;
62513498266Sopenharmony_ci      }
62613498266Sopenharmony_ci    }
62713498266Sopenharmony_ci    else {
62813498266Sopenharmony_ci      puts("Usage: tftpd [option]\n"
62913498266Sopenharmony_ci           " --version\n"
63013498266Sopenharmony_ci           " --logfile [file]\n"
63113498266Sopenharmony_ci           " --logdir [directory]\n"
63213498266Sopenharmony_ci           " --pidfile [file]\n"
63313498266Sopenharmony_ci           " --portfile [file]\n"
63413498266Sopenharmony_ci           " --ipv4\n"
63513498266Sopenharmony_ci           " --ipv6\n"
63613498266Sopenharmony_ci           " --port [port]\n"
63713498266Sopenharmony_ci           " --srcdir [path]");
63813498266Sopenharmony_ci      return 0;
63913498266Sopenharmony_ci    }
64013498266Sopenharmony_ci  }
64113498266Sopenharmony_ci
64213498266Sopenharmony_ci  msnprintf(loglockfile, sizeof(loglockfile), "%s/%s/tftp-%s.lock",
64313498266Sopenharmony_ci            logdir, SERVERLOGS_LOCKDIR, ipv_inuse);
64413498266Sopenharmony_ci
64513498266Sopenharmony_ci#ifdef _WIN32
64613498266Sopenharmony_ci  win32_init();
64713498266Sopenharmony_ci  atexit(win32_cleanup);
64813498266Sopenharmony_ci#endif
64913498266Sopenharmony_ci
65013498266Sopenharmony_ci  install_signal_handlers(true);
65113498266Sopenharmony_ci
65213498266Sopenharmony_ci#ifdef ENABLE_IPV6
65313498266Sopenharmony_ci  if(!use_ipv6)
65413498266Sopenharmony_ci#endif
65513498266Sopenharmony_ci    sock = socket(AF_INET, SOCK_DGRAM, 0);
65613498266Sopenharmony_ci#ifdef ENABLE_IPV6
65713498266Sopenharmony_ci  else
65813498266Sopenharmony_ci    sock = socket(AF_INET6, SOCK_DGRAM, 0);
65913498266Sopenharmony_ci#endif
66013498266Sopenharmony_ci
66113498266Sopenharmony_ci  if(CURL_SOCKET_BAD == sock) {
66213498266Sopenharmony_ci    error = SOCKERRNO;
66313498266Sopenharmony_ci    logmsg("Error creating socket: (%d) %s", error, sstrerror(error));
66413498266Sopenharmony_ci    result = 1;
66513498266Sopenharmony_ci    goto tftpd_cleanup;
66613498266Sopenharmony_ci  }
66713498266Sopenharmony_ci
66813498266Sopenharmony_ci  flag = 1;
66913498266Sopenharmony_ci  if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
67013498266Sopenharmony_ci            (void *)&flag, sizeof(flag))) {
67113498266Sopenharmony_ci    error = SOCKERRNO;
67213498266Sopenharmony_ci    logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
67313498266Sopenharmony_ci           error, sstrerror(error));
67413498266Sopenharmony_ci    result = 1;
67513498266Sopenharmony_ci    goto tftpd_cleanup;
67613498266Sopenharmony_ci  }
67713498266Sopenharmony_ci
67813498266Sopenharmony_ci#ifdef ENABLE_IPV6
67913498266Sopenharmony_ci  if(!use_ipv6) {
68013498266Sopenharmony_ci#endif
68113498266Sopenharmony_ci    memset(&me.sa4, 0, sizeof(me.sa4));
68213498266Sopenharmony_ci    me.sa4.sin_family = AF_INET;
68313498266Sopenharmony_ci    me.sa4.sin_addr.s_addr = INADDR_ANY;
68413498266Sopenharmony_ci    me.sa4.sin_port = htons(port);
68513498266Sopenharmony_ci    rc = bind(sock, &me.sa, sizeof(me.sa4));
68613498266Sopenharmony_ci#ifdef ENABLE_IPV6
68713498266Sopenharmony_ci  }
68813498266Sopenharmony_ci  else {
68913498266Sopenharmony_ci    memset(&me.sa6, 0, sizeof(me.sa6));
69013498266Sopenharmony_ci    me.sa6.sin6_family = AF_INET6;
69113498266Sopenharmony_ci    me.sa6.sin6_addr = in6addr_any;
69213498266Sopenharmony_ci    me.sa6.sin6_port = htons(port);
69313498266Sopenharmony_ci    rc = bind(sock, &me.sa, sizeof(me.sa6));
69413498266Sopenharmony_ci  }
69513498266Sopenharmony_ci#endif /* ENABLE_IPV6 */
69613498266Sopenharmony_ci  if(0 != rc) {
69713498266Sopenharmony_ci    error = SOCKERRNO;
69813498266Sopenharmony_ci    logmsg("Error binding socket on port %hu: (%d) %s", port, error,
69913498266Sopenharmony_ci           sstrerror(error));
70013498266Sopenharmony_ci    result = 1;
70113498266Sopenharmony_ci    goto tftpd_cleanup;
70213498266Sopenharmony_ci  }
70313498266Sopenharmony_ci
70413498266Sopenharmony_ci  if(!port) {
70513498266Sopenharmony_ci    /* The system was supposed to choose a port number, figure out which
70613498266Sopenharmony_ci       port we actually got and update the listener port value with it. */
70713498266Sopenharmony_ci    curl_socklen_t la_size;
70813498266Sopenharmony_ci    srvr_sockaddr_union_t localaddr;
70913498266Sopenharmony_ci#ifdef ENABLE_IPV6
71013498266Sopenharmony_ci    if(!use_ipv6)
71113498266Sopenharmony_ci#endif
71213498266Sopenharmony_ci      la_size = sizeof(localaddr.sa4);
71313498266Sopenharmony_ci#ifdef ENABLE_IPV6
71413498266Sopenharmony_ci    else
71513498266Sopenharmony_ci      la_size = sizeof(localaddr.sa6);
71613498266Sopenharmony_ci#endif
71713498266Sopenharmony_ci    memset(&localaddr.sa, 0, (size_t)la_size);
71813498266Sopenharmony_ci    if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
71913498266Sopenharmony_ci      error = SOCKERRNO;
72013498266Sopenharmony_ci      logmsg("getsockname() failed with error: (%d) %s",
72113498266Sopenharmony_ci             error, sstrerror(error));
72213498266Sopenharmony_ci      sclose(sock);
72313498266Sopenharmony_ci      goto tftpd_cleanup;
72413498266Sopenharmony_ci    }
72513498266Sopenharmony_ci    switch(localaddr.sa.sa_family) {
72613498266Sopenharmony_ci    case AF_INET:
72713498266Sopenharmony_ci      port = ntohs(localaddr.sa4.sin_port);
72813498266Sopenharmony_ci      break;
72913498266Sopenharmony_ci#ifdef ENABLE_IPV6
73013498266Sopenharmony_ci    case AF_INET6:
73113498266Sopenharmony_ci      port = ntohs(localaddr.sa6.sin6_port);
73213498266Sopenharmony_ci      break;
73313498266Sopenharmony_ci#endif
73413498266Sopenharmony_ci    default:
73513498266Sopenharmony_ci      break;
73613498266Sopenharmony_ci    }
73713498266Sopenharmony_ci    if(!port) {
73813498266Sopenharmony_ci      /* Real failure, listener port shall not be zero beyond this point. */
73913498266Sopenharmony_ci      logmsg("Apparently getsockname() succeeded, with listener port zero.");
74013498266Sopenharmony_ci      logmsg("A valid reason for this failure is a binary built without");
74113498266Sopenharmony_ci      logmsg("proper network library linkage. This might not be the only");
74213498266Sopenharmony_ci      logmsg("reason, but double check it before anything else.");
74313498266Sopenharmony_ci      result = 2;
74413498266Sopenharmony_ci      goto tftpd_cleanup;
74513498266Sopenharmony_ci    }
74613498266Sopenharmony_ci  }
74713498266Sopenharmony_ci
74813498266Sopenharmony_ci  wrotepidfile = write_pidfile(pidname);
74913498266Sopenharmony_ci  if(!wrotepidfile) {
75013498266Sopenharmony_ci    result = 1;
75113498266Sopenharmony_ci    goto tftpd_cleanup;
75213498266Sopenharmony_ci  }
75313498266Sopenharmony_ci
75413498266Sopenharmony_ci  if(portname) {
75513498266Sopenharmony_ci    wroteportfile = write_portfile(portname, port);
75613498266Sopenharmony_ci    if(!wroteportfile) {
75713498266Sopenharmony_ci      result = 1;
75813498266Sopenharmony_ci      goto tftpd_cleanup;
75913498266Sopenharmony_ci    }
76013498266Sopenharmony_ci  }
76113498266Sopenharmony_ci
76213498266Sopenharmony_ci  logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port);
76313498266Sopenharmony_ci
76413498266Sopenharmony_ci  for(;;) {
76513498266Sopenharmony_ci    fromlen = sizeof(from);
76613498266Sopenharmony_ci#ifdef ENABLE_IPV6
76713498266Sopenharmony_ci    if(!use_ipv6)
76813498266Sopenharmony_ci#endif
76913498266Sopenharmony_ci      fromlen = sizeof(from.sa4);
77013498266Sopenharmony_ci#ifdef ENABLE_IPV6
77113498266Sopenharmony_ci    else
77213498266Sopenharmony_ci      fromlen = sizeof(from.sa6);
77313498266Sopenharmony_ci#endif
77413498266Sopenharmony_ci    n = (ssize_t)recvfrom(sock, &buf.storage[0], sizeof(buf.storage), 0,
77513498266Sopenharmony_ci                          &from.sa, &fromlen);
77613498266Sopenharmony_ci    if(got_exit_signal)
77713498266Sopenharmony_ci      break;
77813498266Sopenharmony_ci    if(n < 0) {
77913498266Sopenharmony_ci      logmsg("recvfrom");
78013498266Sopenharmony_ci      result = 3;
78113498266Sopenharmony_ci      break;
78213498266Sopenharmony_ci    }
78313498266Sopenharmony_ci
78413498266Sopenharmony_ci    set_advisor_read_lock(loglockfile);
78513498266Sopenharmony_ci    serverlogslocked = 1;
78613498266Sopenharmony_ci
78713498266Sopenharmony_ci#ifdef ENABLE_IPV6
78813498266Sopenharmony_ci    if(!use_ipv6) {
78913498266Sopenharmony_ci#endif
79013498266Sopenharmony_ci      from.sa4.sin_family = AF_INET;
79113498266Sopenharmony_ci      peer = socket(AF_INET, SOCK_DGRAM, 0);
79213498266Sopenharmony_ci      if(CURL_SOCKET_BAD == peer) {
79313498266Sopenharmony_ci        logmsg("socket");
79413498266Sopenharmony_ci        result = 2;
79513498266Sopenharmony_ci        break;
79613498266Sopenharmony_ci      }
79713498266Sopenharmony_ci      if(connect(peer, &from.sa, sizeof(from.sa4)) < 0) {
79813498266Sopenharmony_ci        logmsg("connect: fail");
79913498266Sopenharmony_ci        result = 1;
80013498266Sopenharmony_ci        break;
80113498266Sopenharmony_ci      }
80213498266Sopenharmony_ci#ifdef ENABLE_IPV6
80313498266Sopenharmony_ci    }
80413498266Sopenharmony_ci    else {
80513498266Sopenharmony_ci      from.sa6.sin6_family = AF_INET6;
80613498266Sopenharmony_ci      peer = socket(AF_INET6, SOCK_DGRAM, 0);
80713498266Sopenharmony_ci      if(CURL_SOCKET_BAD == peer) {
80813498266Sopenharmony_ci        logmsg("socket");
80913498266Sopenharmony_ci        result = 2;
81013498266Sopenharmony_ci        break;
81113498266Sopenharmony_ci      }
81213498266Sopenharmony_ci      if(connect(peer, &from.sa, sizeof(from.sa6)) < 0) {
81313498266Sopenharmony_ci        logmsg("connect: fail");
81413498266Sopenharmony_ci        result = 1;
81513498266Sopenharmony_ci        break;
81613498266Sopenharmony_ci      }
81713498266Sopenharmony_ci    }
81813498266Sopenharmony_ci#endif
81913498266Sopenharmony_ci
82013498266Sopenharmony_ci    maxtimeout = 5*TIMEOUT;
82113498266Sopenharmony_ci
82213498266Sopenharmony_ci    tp = &buf.hdr;
82313498266Sopenharmony_ci    tp->th_opcode = ntohs(tp->th_opcode);
82413498266Sopenharmony_ci    if(tp->th_opcode == opcode_RRQ || tp->th_opcode == opcode_WRQ) {
82513498266Sopenharmony_ci      memset(&test, 0, sizeof(test));
82613498266Sopenharmony_ci      if(do_tftp(&test, tp, n) < 0)
82713498266Sopenharmony_ci        break;
82813498266Sopenharmony_ci      free(test.buffer);
82913498266Sopenharmony_ci    }
83013498266Sopenharmony_ci    sclose(peer);
83113498266Sopenharmony_ci    peer = CURL_SOCKET_BAD;
83213498266Sopenharmony_ci
83313498266Sopenharmony_ci    if(got_exit_signal)
83413498266Sopenharmony_ci      break;
83513498266Sopenharmony_ci
83613498266Sopenharmony_ci    if(serverlogslocked) {
83713498266Sopenharmony_ci      serverlogslocked = 0;
83813498266Sopenharmony_ci      clear_advisor_read_lock(loglockfile);
83913498266Sopenharmony_ci    }
84013498266Sopenharmony_ci
84113498266Sopenharmony_ci    logmsg("end of one transfer");
84213498266Sopenharmony_ci
84313498266Sopenharmony_ci  }
84413498266Sopenharmony_ci
84513498266Sopenharmony_citftpd_cleanup:
84613498266Sopenharmony_ci
84713498266Sopenharmony_ci  if(test.ofile > 0)
84813498266Sopenharmony_ci    close(test.ofile);
84913498266Sopenharmony_ci
85013498266Sopenharmony_ci  if((peer != sock) && (peer != CURL_SOCKET_BAD))
85113498266Sopenharmony_ci    sclose(peer);
85213498266Sopenharmony_ci
85313498266Sopenharmony_ci  if(sock != CURL_SOCKET_BAD)
85413498266Sopenharmony_ci    sclose(sock);
85513498266Sopenharmony_ci
85613498266Sopenharmony_ci  if(got_exit_signal)
85713498266Sopenharmony_ci    logmsg("signalled to die");
85813498266Sopenharmony_ci
85913498266Sopenharmony_ci  if(wrotepidfile)
86013498266Sopenharmony_ci    unlink(pidname);
86113498266Sopenharmony_ci  if(wroteportfile)
86213498266Sopenharmony_ci    unlink(portname);
86313498266Sopenharmony_ci
86413498266Sopenharmony_ci  if(serverlogslocked) {
86513498266Sopenharmony_ci    serverlogslocked = 0;
86613498266Sopenharmony_ci    clear_advisor_read_lock(loglockfile);
86713498266Sopenharmony_ci  }
86813498266Sopenharmony_ci
86913498266Sopenharmony_ci  restore_signal_handlers(true);
87013498266Sopenharmony_ci
87113498266Sopenharmony_ci  if(got_exit_signal) {
87213498266Sopenharmony_ci    logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)",
87313498266Sopenharmony_ci           ipv_inuse, (int)port, (long)getpid(), exit_signal);
87413498266Sopenharmony_ci    /*
87513498266Sopenharmony_ci     * To properly set the return status of the process we
87613498266Sopenharmony_ci     * must raise the same signal SIGINT or SIGTERM that we
87713498266Sopenharmony_ci     * caught and let the old handler take care of it.
87813498266Sopenharmony_ci     */
87913498266Sopenharmony_ci    raise(exit_signal);
88013498266Sopenharmony_ci  }
88113498266Sopenharmony_ci
88213498266Sopenharmony_ci  logmsg("========> tftpd quits");
88313498266Sopenharmony_ci  return result;
88413498266Sopenharmony_ci}
88513498266Sopenharmony_ci
88613498266Sopenharmony_ci/*
88713498266Sopenharmony_ci * Handle initial connection protocol.
88813498266Sopenharmony_ci */
88913498266Sopenharmony_cistatic int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size)
89013498266Sopenharmony_ci{
89113498266Sopenharmony_ci  char *cp;
89213498266Sopenharmony_ci  int first = 1, ecode;
89313498266Sopenharmony_ci  const struct formats *pf;
89413498266Sopenharmony_ci  char *filename, *mode = NULL;
89513498266Sopenharmony_ci#ifdef USE_WINSOCK
89613498266Sopenharmony_ci  DWORD recvtimeout, recvtimeoutbak;
89713498266Sopenharmony_ci#endif
89813498266Sopenharmony_ci  const char *option = "mode"; /* mode is implicit */
89913498266Sopenharmony_ci  int toggle = 1;
90013498266Sopenharmony_ci  FILE *server;
90113498266Sopenharmony_ci  char dumpfile[256];
90213498266Sopenharmony_ci
90313498266Sopenharmony_ci  msnprintf(dumpfile, sizeof(dumpfile), "%s/%s", logdir, REQUEST_DUMP);
90413498266Sopenharmony_ci
90513498266Sopenharmony_ci  /* Open request dump file. */
90613498266Sopenharmony_ci  server = fopen(dumpfile, "ab");
90713498266Sopenharmony_ci  if(!server) {
90813498266Sopenharmony_ci    int error = errno;
90913498266Sopenharmony_ci    logmsg("fopen() failed with error: %d %s", error, strerror(error));
91013498266Sopenharmony_ci    logmsg("Error opening file: %s", dumpfile);
91113498266Sopenharmony_ci    return -1;
91213498266Sopenharmony_ci  }
91313498266Sopenharmony_ci
91413498266Sopenharmony_ci  /* store input protocol */
91513498266Sopenharmony_ci  fprintf(server, "opcode = %x\n", tp->th_opcode);
91613498266Sopenharmony_ci
91713498266Sopenharmony_ci  cp = (char *)&tp->th_stuff;
91813498266Sopenharmony_ci  filename = cp;
91913498266Sopenharmony_ci  do {
92013498266Sopenharmony_ci    bool endofit = true;
92113498266Sopenharmony_ci    while(cp < &buf.storage[size]) {
92213498266Sopenharmony_ci      if(*cp == '\0') {
92313498266Sopenharmony_ci        endofit = false;
92413498266Sopenharmony_ci        break;
92513498266Sopenharmony_ci      }
92613498266Sopenharmony_ci      cp++;
92713498266Sopenharmony_ci    }
92813498266Sopenharmony_ci    if(endofit)
92913498266Sopenharmony_ci      /* no more options */
93013498266Sopenharmony_ci      break;
93113498266Sopenharmony_ci
93213498266Sopenharmony_ci    /* before increasing pointer, make sure it is still within the legal
93313498266Sopenharmony_ci       space */
93413498266Sopenharmony_ci    if((cp + 1) < &buf.storage[size]) {
93513498266Sopenharmony_ci      ++cp;
93613498266Sopenharmony_ci      if(first) {
93713498266Sopenharmony_ci        /* store the mode since we need it later */
93813498266Sopenharmony_ci        mode = cp;
93913498266Sopenharmony_ci        first = 0;
94013498266Sopenharmony_ci      }
94113498266Sopenharmony_ci      if(toggle)
94213498266Sopenharmony_ci        /* name/value pair: */
94313498266Sopenharmony_ci        fprintf(server, "%s = %s\n", option, cp);
94413498266Sopenharmony_ci      else {
94513498266Sopenharmony_ci        /* store the name pointer */
94613498266Sopenharmony_ci        option = cp;
94713498266Sopenharmony_ci      }
94813498266Sopenharmony_ci      toggle ^= 1;
94913498266Sopenharmony_ci    }
95013498266Sopenharmony_ci    else
95113498266Sopenharmony_ci      /* No more options */
95213498266Sopenharmony_ci      break;
95313498266Sopenharmony_ci  } while(1);
95413498266Sopenharmony_ci
95513498266Sopenharmony_ci  if(*cp) {
95613498266Sopenharmony_ci    nak(EBADOP);
95713498266Sopenharmony_ci    fclose(server);
95813498266Sopenharmony_ci    return 3;
95913498266Sopenharmony_ci  }
96013498266Sopenharmony_ci
96113498266Sopenharmony_ci  /* store input protocol */
96213498266Sopenharmony_ci  fprintf(server, "filename = %s\n", filename);
96313498266Sopenharmony_ci
96413498266Sopenharmony_ci  for(cp = mode; cp && *cp; cp++)
96513498266Sopenharmony_ci    if(ISUPPER(*cp))
96613498266Sopenharmony_ci      *cp = (char)tolower((int)*cp);
96713498266Sopenharmony_ci
96813498266Sopenharmony_ci  /* store input protocol */
96913498266Sopenharmony_ci  fclose(server);
97013498266Sopenharmony_ci
97113498266Sopenharmony_ci  for(pf = formata; pf->f_mode; pf++)
97213498266Sopenharmony_ci    if(strcmp(pf->f_mode, mode) == 0)
97313498266Sopenharmony_ci      break;
97413498266Sopenharmony_ci  if(!pf->f_mode) {
97513498266Sopenharmony_ci    nak(EBADOP);
97613498266Sopenharmony_ci    return 2;
97713498266Sopenharmony_ci  }
97813498266Sopenharmony_ci  ecode = validate_access(test, filename, tp->th_opcode);
97913498266Sopenharmony_ci  if(ecode) {
98013498266Sopenharmony_ci    nak(ecode);
98113498266Sopenharmony_ci    return 1;
98213498266Sopenharmony_ci  }
98313498266Sopenharmony_ci
98413498266Sopenharmony_ci#ifdef USE_WINSOCK
98513498266Sopenharmony_ci  recvtimeout = sizeof(recvtimeoutbak);
98613498266Sopenharmony_ci  getsockopt(peer, SOL_SOCKET, SO_RCVTIMEO,
98713498266Sopenharmony_ci             (char *)&recvtimeoutbak, (int *)&recvtimeout);
98813498266Sopenharmony_ci  recvtimeout = TIMEOUT*1000;
98913498266Sopenharmony_ci  setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO,
99013498266Sopenharmony_ci             (const char *)&recvtimeout, sizeof(recvtimeout));
99113498266Sopenharmony_ci#endif
99213498266Sopenharmony_ci
99313498266Sopenharmony_ci  if(tp->th_opcode == opcode_WRQ)
99413498266Sopenharmony_ci    recvtftp(test, pf);
99513498266Sopenharmony_ci  else
99613498266Sopenharmony_ci    sendtftp(test, pf);
99713498266Sopenharmony_ci
99813498266Sopenharmony_ci#ifdef USE_WINSOCK
99913498266Sopenharmony_ci  recvtimeout = recvtimeoutbak;
100013498266Sopenharmony_ci  setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO,
100113498266Sopenharmony_ci             (const char *)&recvtimeout, sizeof(recvtimeout));
100213498266Sopenharmony_ci#endif
100313498266Sopenharmony_ci
100413498266Sopenharmony_ci  return 0;
100513498266Sopenharmony_ci}
100613498266Sopenharmony_ci
100713498266Sopenharmony_ci/* Based on the testno, parse the correct server commands. */
100813498266Sopenharmony_cistatic int parse_servercmd(struct testcase *req)
100913498266Sopenharmony_ci{
101013498266Sopenharmony_ci  FILE *stream;
101113498266Sopenharmony_ci  int error;
101213498266Sopenharmony_ci
101313498266Sopenharmony_ci  stream = test2fopen(req->testno, logdir);
101413498266Sopenharmony_ci  if(!stream) {
101513498266Sopenharmony_ci    error = errno;
101613498266Sopenharmony_ci    logmsg("fopen() failed with error: %d %s", error, strerror(error));
101713498266Sopenharmony_ci    logmsg("  Couldn't open test file %ld", req->testno);
101813498266Sopenharmony_ci    return 1; /* done */
101913498266Sopenharmony_ci  }
102013498266Sopenharmony_ci  else {
102113498266Sopenharmony_ci    char *orgcmd = NULL;
102213498266Sopenharmony_ci    char *cmd = NULL;
102313498266Sopenharmony_ci    size_t cmdsize = 0;
102413498266Sopenharmony_ci    int num = 0;
102513498266Sopenharmony_ci
102613498266Sopenharmony_ci    /* get the custom server control "commands" */
102713498266Sopenharmony_ci    error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
102813498266Sopenharmony_ci    fclose(stream);
102913498266Sopenharmony_ci    if(error) {
103013498266Sopenharmony_ci      logmsg("getpart() failed with error: %d", error);
103113498266Sopenharmony_ci      return 1; /* done */
103213498266Sopenharmony_ci    }
103313498266Sopenharmony_ci
103413498266Sopenharmony_ci    cmd = orgcmd;
103513498266Sopenharmony_ci    while(cmd && cmdsize) {
103613498266Sopenharmony_ci      char *check;
103713498266Sopenharmony_ci      if(1 == sscanf(cmd, "writedelay: %d", &num)) {
103813498266Sopenharmony_ci        logmsg("instructed to delay %d secs between packets", num);
103913498266Sopenharmony_ci        req->writedelay = num;
104013498266Sopenharmony_ci      }
104113498266Sopenharmony_ci      else {
104213498266Sopenharmony_ci        logmsg("Unknown <servercmd> instruction found: %s", cmd);
104313498266Sopenharmony_ci      }
104413498266Sopenharmony_ci      /* try to deal with CRLF or just LF */
104513498266Sopenharmony_ci      check = strchr(cmd, '\r');
104613498266Sopenharmony_ci      if(!check)
104713498266Sopenharmony_ci        check = strchr(cmd, '\n');
104813498266Sopenharmony_ci
104913498266Sopenharmony_ci      if(check) {
105013498266Sopenharmony_ci        /* get to the letter following the newline */
105113498266Sopenharmony_ci        while((*check == '\r') || (*check == '\n'))
105213498266Sopenharmony_ci          check++;
105313498266Sopenharmony_ci
105413498266Sopenharmony_ci        if(!*check)
105513498266Sopenharmony_ci          /* if we reached a zero, get out */
105613498266Sopenharmony_ci          break;
105713498266Sopenharmony_ci        cmd = check;
105813498266Sopenharmony_ci      }
105913498266Sopenharmony_ci      else
106013498266Sopenharmony_ci        break;
106113498266Sopenharmony_ci    }
106213498266Sopenharmony_ci    free(orgcmd);
106313498266Sopenharmony_ci  }
106413498266Sopenharmony_ci
106513498266Sopenharmony_ci  return 0; /* OK! */
106613498266Sopenharmony_ci}
106713498266Sopenharmony_ci
106813498266Sopenharmony_ci
106913498266Sopenharmony_ci/*
107013498266Sopenharmony_ci * Validate file access.
107113498266Sopenharmony_ci */
107213498266Sopenharmony_cistatic int validate_access(struct testcase *test,
107313498266Sopenharmony_ci                           const char *filename, int mode)
107413498266Sopenharmony_ci{
107513498266Sopenharmony_ci  char *ptr;
107613498266Sopenharmony_ci
107713498266Sopenharmony_ci  logmsg("trying to get file: %s mode %x", filename, mode);
107813498266Sopenharmony_ci
107913498266Sopenharmony_ci  if(!strncmp("verifiedserver", filename, 14)) {
108013498266Sopenharmony_ci    char weare[128];
108113498266Sopenharmony_ci    size_t count = msnprintf(weare, sizeof(weare), "WE ROOLZ: %"
108213498266Sopenharmony_ci                             CURL_FORMAT_CURL_OFF_T "\r\n", our_getpid());
108313498266Sopenharmony_ci
108413498266Sopenharmony_ci    logmsg("Are-we-friendly question received");
108513498266Sopenharmony_ci    test->buffer = strdup(weare);
108613498266Sopenharmony_ci    test->rptr = test->buffer; /* set read pointer */
108713498266Sopenharmony_ci    test->bufsize = count;    /* set total count */
108813498266Sopenharmony_ci    test->rcount = count;     /* set data left to read */
108913498266Sopenharmony_ci    return 0; /* fine */
109013498266Sopenharmony_ci  }
109113498266Sopenharmony_ci
109213498266Sopenharmony_ci  /* find the last slash */
109313498266Sopenharmony_ci  ptr = strrchr(filename, '/');
109413498266Sopenharmony_ci
109513498266Sopenharmony_ci  if(ptr) {
109613498266Sopenharmony_ci    char partbuf[80]="data";
109713498266Sopenharmony_ci    long partno;
109813498266Sopenharmony_ci    long testno;
109913498266Sopenharmony_ci    FILE *stream;
110013498266Sopenharmony_ci
110113498266Sopenharmony_ci    ptr++; /* skip the slash */
110213498266Sopenharmony_ci
110313498266Sopenharmony_ci    /* skip all non-numericals following the slash */
110413498266Sopenharmony_ci    while(*ptr && !ISDIGIT(*ptr))
110513498266Sopenharmony_ci      ptr++;
110613498266Sopenharmony_ci
110713498266Sopenharmony_ci    /* get the number */
110813498266Sopenharmony_ci    testno = strtol(ptr, &ptr, 10);
110913498266Sopenharmony_ci
111013498266Sopenharmony_ci    if(testno > 10000) {
111113498266Sopenharmony_ci      partno = testno % 10000;
111213498266Sopenharmony_ci      testno /= 10000;
111313498266Sopenharmony_ci    }
111413498266Sopenharmony_ci    else
111513498266Sopenharmony_ci      partno = 0;
111613498266Sopenharmony_ci
111713498266Sopenharmony_ci
111813498266Sopenharmony_ci    logmsg("requested test number %ld part %ld", testno, partno);
111913498266Sopenharmony_ci
112013498266Sopenharmony_ci    test->testno = testno;
112113498266Sopenharmony_ci
112213498266Sopenharmony_ci    (void)parse_servercmd(test);
112313498266Sopenharmony_ci
112413498266Sopenharmony_ci    stream = test2fopen(testno, logdir);
112513498266Sopenharmony_ci
112613498266Sopenharmony_ci    if(0 != partno)
112713498266Sopenharmony_ci      msnprintf(partbuf, sizeof(partbuf), "data%ld", partno);
112813498266Sopenharmony_ci
112913498266Sopenharmony_ci    if(!stream) {
113013498266Sopenharmony_ci      int error = errno;
113113498266Sopenharmony_ci      logmsg("fopen() failed with error: %d %s", error, strerror(error));
113213498266Sopenharmony_ci      logmsg("Couldn't open test file for test: %ld", testno);
113313498266Sopenharmony_ci      return EACCESS;
113413498266Sopenharmony_ci    }
113513498266Sopenharmony_ci    else {
113613498266Sopenharmony_ci      size_t count;
113713498266Sopenharmony_ci      int error = getpart(&test->buffer, &count, "reply", partbuf, stream);
113813498266Sopenharmony_ci      fclose(stream);
113913498266Sopenharmony_ci      if(error) {
114013498266Sopenharmony_ci        logmsg("getpart() failed with error: %d", error);
114113498266Sopenharmony_ci        return EACCESS;
114213498266Sopenharmony_ci      }
114313498266Sopenharmony_ci      if(test->buffer) {
114413498266Sopenharmony_ci        test->rptr = test->buffer; /* set read pointer */
114513498266Sopenharmony_ci        test->bufsize = count;    /* set total count */
114613498266Sopenharmony_ci        test->rcount = count;     /* set data left to read */
114713498266Sopenharmony_ci      }
114813498266Sopenharmony_ci      else
114913498266Sopenharmony_ci        return EACCESS;
115013498266Sopenharmony_ci    }
115113498266Sopenharmony_ci  }
115213498266Sopenharmony_ci  else {
115313498266Sopenharmony_ci    logmsg("no slash found in path");
115413498266Sopenharmony_ci    return EACCESS; /* failure */
115513498266Sopenharmony_ci  }
115613498266Sopenharmony_ci
115713498266Sopenharmony_ci  logmsg("file opened and all is good");
115813498266Sopenharmony_ci  return 0;
115913498266Sopenharmony_ci}
116013498266Sopenharmony_ci
116113498266Sopenharmony_ci/*
116213498266Sopenharmony_ci * Send the requested file.
116313498266Sopenharmony_ci */
116413498266Sopenharmony_cistatic void sendtftp(struct testcase *test, const struct formats *pf)
116513498266Sopenharmony_ci{
116613498266Sopenharmony_ci  int size;
116713498266Sopenharmony_ci  ssize_t n;
116813498266Sopenharmony_ci  /* These are volatile to live through a siglongjmp */
116913498266Sopenharmony_ci  volatile unsigned short sendblock; /* block count */
117013498266Sopenharmony_ci  struct tftphdr * volatile sdp = r_init(); /* data buffer */
117113498266Sopenharmony_ci  struct tftphdr * const sap = &ackbuf.hdr; /* ack buffer */
117213498266Sopenharmony_ci
117313498266Sopenharmony_ci  sendblock = 1;
117413498266Sopenharmony_ci#if defined(HAVE_ALARM) && defined(SIGALRM)
117513498266Sopenharmony_ci  mysignal(SIGALRM, timer);
117613498266Sopenharmony_ci#endif
117713498266Sopenharmony_ci  do {
117813498266Sopenharmony_ci    size = readit(test, (struct tftphdr **)&sdp, pf->f_convert);
117913498266Sopenharmony_ci    if(size < 0) {
118013498266Sopenharmony_ci      nak(errno + 100);
118113498266Sopenharmony_ci      return;
118213498266Sopenharmony_ci    }
118313498266Sopenharmony_ci    sdp->th_opcode = htons((unsigned short)opcode_DATA);
118413498266Sopenharmony_ci    sdp->th_block = htons(sendblock);
118513498266Sopenharmony_ci    timeout = 0;
118613498266Sopenharmony_ci#ifdef HAVE_SIGSETJMP
118713498266Sopenharmony_ci    (void) sigsetjmp(timeoutbuf, 1);
118813498266Sopenharmony_ci#endif
118913498266Sopenharmony_ci    if(test->writedelay) {
119013498266Sopenharmony_ci      logmsg("Pausing %d seconds before %d bytes", test->writedelay,
119113498266Sopenharmony_ci             size);
119213498266Sopenharmony_ci      wait_ms(1000*test->writedelay);
119313498266Sopenharmony_ci    }
119413498266Sopenharmony_ci
119513498266Sopenharmony_cisend_data:
119613498266Sopenharmony_ci    logmsg("write");
119713498266Sopenharmony_ci    if(swrite(peer, sdp, size + 4) != size + 4) {
119813498266Sopenharmony_ci      logmsg("write: fail");
119913498266Sopenharmony_ci      return;
120013498266Sopenharmony_ci    }
120113498266Sopenharmony_ci    read_ahead(test, pf->f_convert);
120213498266Sopenharmony_ci    for(;;) {
120313498266Sopenharmony_ci#ifdef HAVE_ALARM
120413498266Sopenharmony_ci      alarm(rexmtval);        /* read the ack */
120513498266Sopenharmony_ci#endif
120613498266Sopenharmony_ci      logmsg("read");
120713498266Sopenharmony_ci      n = sread(peer, &ackbuf.storage[0], sizeof(ackbuf.storage));
120813498266Sopenharmony_ci      logmsg("read: %zd", n);
120913498266Sopenharmony_ci#ifdef HAVE_ALARM
121013498266Sopenharmony_ci      alarm(0);
121113498266Sopenharmony_ci#endif
121213498266Sopenharmony_ci      if(got_exit_signal)
121313498266Sopenharmony_ci        return;
121413498266Sopenharmony_ci      if(n < 0) {
121513498266Sopenharmony_ci        logmsg("read: fail");
121613498266Sopenharmony_ci        return;
121713498266Sopenharmony_ci      }
121813498266Sopenharmony_ci      sap->th_opcode = ntohs((unsigned short)sap->th_opcode);
121913498266Sopenharmony_ci      sap->th_block = ntohs(sap->th_block);
122013498266Sopenharmony_ci
122113498266Sopenharmony_ci      if(sap->th_opcode == opcode_ERROR) {
122213498266Sopenharmony_ci        logmsg("got ERROR");
122313498266Sopenharmony_ci        return;
122413498266Sopenharmony_ci      }
122513498266Sopenharmony_ci
122613498266Sopenharmony_ci      if(sap->th_opcode == opcode_ACK) {
122713498266Sopenharmony_ci        if(sap->th_block == sendblock) {
122813498266Sopenharmony_ci          break;
122913498266Sopenharmony_ci        }
123013498266Sopenharmony_ci        /* Re-synchronize with the other side */
123113498266Sopenharmony_ci        (void) synchnet(peer);
123213498266Sopenharmony_ci        if(sap->th_block == (sendblock-1)) {
123313498266Sopenharmony_ci          goto send_data;
123413498266Sopenharmony_ci        }
123513498266Sopenharmony_ci      }
123613498266Sopenharmony_ci
123713498266Sopenharmony_ci    }
123813498266Sopenharmony_ci    sendblock++;
123913498266Sopenharmony_ci  } while(size == SEGSIZE);
124013498266Sopenharmony_ci}
124113498266Sopenharmony_ci
124213498266Sopenharmony_ci/*
124313498266Sopenharmony_ci * Receive a file.
124413498266Sopenharmony_ci */
124513498266Sopenharmony_cistatic void recvtftp(struct testcase *test, const struct formats *pf)
124613498266Sopenharmony_ci{
124713498266Sopenharmony_ci  ssize_t n, size;
124813498266Sopenharmony_ci  /* These are volatile to live through a siglongjmp */
124913498266Sopenharmony_ci  volatile unsigned short recvblock; /* block count */
125013498266Sopenharmony_ci  struct tftphdr * volatile rdp;     /* data buffer */
125113498266Sopenharmony_ci  struct tftphdr *rap;      /* ack buffer */
125213498266Sopenharmony_ci
125313498266Sopenharmony_ci  recvblock = 0;
125413498266Sopenharmony_ci  rdp = w_init();
125513498266Sopenharmony_ci#if defined(HAVE_ALARM) && defined(SIGALRM)
125613498266Sopenharmony_ci  mysignal(SIGALRM, timer);
125713498266Sopenharmony_ci#endif
125813498266Sopenharmony_ci  rap = &ackbuf.hdr;
125913498266Sopenharmony_ci  do {
126013498266Sopenharmony_ci    timeout = 0;
126113498266Sopenharmony_ci    rap->th_opcode = htons((unsigned short)opcode_ACK);
126213498266Sopenharmony_ci    rap->th_block = htons(recvblock);
126313498266Sopenharmony_ci    recvblock++;
126413498266Sopenharmony_ci#ifdef HAVE_SIGSETJMP
126513498266Sopenharmony_ci    (void) sigsetjmp(timeoutbuf, 1);
126613498266Sopenharmony_ci#endif
126713498266Sopenharmony_cisend_ack:
126813498266Sopenharmony_ci    logmsg("write");
126913498266Sopenharmony_ci    if(swrite(peer, &ackbuf.storage[0], 4) != 4) {
127013498266Sopenharmony_ci      logmsg("write: fail");
127113498266Sopenharmony_ci      goto abort;
127213498266Sopenharmony_ci    }
127313498266Sopenharmony_ci    write_behind(test, pf->f_convert);
127413498266Sopenharmony_ci    for(;;) {
127513498266Sopenharmony_ci#ifdef HAVE_ALARM
127613498266Sopenharmony_ci      alarm(rexmtval);
127713498266Sopenharmony_ci#endif
127813498266Sopenharmony_ci      logmsg("read");
127913498266Sopenharmony_ci      n = sread(peer, rdp, PKTSIZE);
128013498266Sopenharmony_ci      logmsg("read: %zd", n);
128113498266Sopenharmony_ci#ifdef HAVE_ALARM
128213498266Sopenharmony_ci      alarm(0);
128313498266Sopenharmony_ci#endif
128413498266Sopenharmony_ci      if(got_exit_signal)
128513498266Sopenharmony_ci        goto abort;
128613498266Sopenharmony_ci      if(n < 0) {                       /* really? */
128713498266Sopenharmony_ci        logmsg("read: fail");
128813498266Sopenharmony_ci        goto abort;
128913498266Sopenharmony_ci      }
129013498266Sopenharmony_ci      rdp->th_opcode = ntohs((unsigned short)rdp->th_opcode);
129113498266Sopenharmony_ci      rdp->th_block = ntohs(rdp->th_block);
129213498266Sopenharmony_ci      if(rdp->th_opcode == opcode_ERROR)
129313498266Sopenharmony_ci        goto abort;
129413498266Sopenharmony_ci      if(rdp->th_opcode == opcode_DATA) {
129513498266Sopenharmony_ci        if(rdp->th_block == recvblock) {
129613498266Sopenharmony_ci          break;                         /* normal */
129713498266Sopenharmony_ci        }
129813498266Sopenharmony_ci        /* Re-synchronize with the other side */
129913498266Sopenharmony_ci        (void) synchnet(peer);
130013498266Sopenharmony_ci        if(rdp->th_block == (recvblock-1))
130113498266Sopenharmony_ci          goto send_ack;                 /* rexmit */
130213498266Sopenharmony_ci      }
130313498266Sopenharmony_ci    }
130413498266Sopenharmony_ci
130513498266Sopenharmony_ci    size = writeit(test, &rdp, (int)(n - 4), pf->f_convert);
130613498266Sopenharmony_ci    if(size != (n-4)) {                 /* ahem */
130713498266Sopenharmony_ci      if(size < 0)
130813498266Sopenharmony_ci        nak(errno + 100);
130913498266Sopenharmony_ci      else
131013498266Sopenharmony_ci        nak(ENOSPACE);
131113498266Sopenharmony_ci      goto abort;
131213498266Sopenharmony_ci    }
131313498266Sopenharmony_ci  } while(size == SEGSIZE);
131413498266Sopenharmony_ci  write_behind(test, pf->f_convert);
131513498266Sopenharmony_ci  /* close the output file as early as possible after upload completion */
131613498266Sopenharmony_ci  if(test->ofile > 0) {
131713498266Sopenharmony_ci    close(test->ofile);
131813498266Sopenharmony_ci    test->ofile = 0;
131913498266Sopenharmony_ci  }
132013498266Sopenharmony_ci
132113498266Sopenharmony_ci  rap->th_opcode = htons((unsigned short)opcode_ACK);  /* send the "final"
132213498266Sopenharmony_ci                                                          ack */
132313498266Sopenharmony_ci  rap->th_block = htons(recvblock);
132413498266Sopenharmony_ci  (void) swrite(peer, &ackbuf.storage[0], 4);
132513498266Sopenharmony_ci#if defined(HAVE_ALARM) && defined(SIGALRM)
132613498266Sopenharmony_ci  mysignal(SIGALRM, justtimeout);        /* just abort read on timeout */
132713498266Sopenharmony_ci  alarm(rexmtval);
132813498266Sopenharmony_ci#endif
132913498266Sopenharmony_ci  /* normally times out and quits */
133013498266Sopenharmony_ci  n = sread(peer, &buf.storage[0], sizeof(buf.storage));
133113498266Sopenharmony_ci#ifdef HAVE_ALARM
133213498266Sopenharmony_ci  alarm(0);
133313498266Sopenharmony_ci#endif
133413498266Sopenharmony_ci  if(got_exit_signal)
133513498266Sopenharmony_ci    goto abort;
133613498266Sopenharmony_ci  if(n >= 4 &&                               /* if read some data */
133713498266Sopenharmony_ci     rdp->th_opcode == opcode_DATA &&        /* and got a data block */
133813498266Sopenharmony_ci     recvblock == rdp->th_block) {           /* then my last ack was lost */
133913498266Sopenharmony_ci    (void) swrite(peer, &ackbuf.storage[0], 4);  /* resend final ack */
134013498266Sopenharmony_ci  }
134113498266Sopenharmony_ciabort:
134213498266Sopenharmony_ci  /* make sure the output file is closed in case of abort */
134313498266Sopenharmony_ci  if(test->ofile > 0) {
134413498266Sopenharmony_ci    close(test->ofile);
134513498266Sopenharmony_ci    test->ofile = 0;
134613498266Sopenharmony_ci  }
134713498266Sopenharmony_ci  return;
134813498266Sopenharmony_ci}
134913498266Sopenharmony_ci
135013498266Sopenharmony_ci/*
135113498266Sopenharmony_ci * Send a nak packet (error message).  Error code passed in is one of the
135213498266Sopenharmony_ci * standard TFTP codes, or a Unix errno offset by 100.
135313498266Sopenharmony_ci */
135413498266Sopenharmony_cistatic void nak(int error)
135513498266Sopenharmony_ci{
135613498266Sopenharmony_ci  struct tftphdr *tp;
135713498266Sopenharmony_ci  int length;
135813498266Sopenharmony_ci  struct errmsg *pe;
135913498266Sopenharmony_ci
136013498266Sopenharmony_ci  tp = &buf.hdr;
136113498266Sopenharmony_ci  tp->th_opcode = htons((unsigned short)opcode_ERROR);
136213498266Sopenharmony_ci  tp->th_code = htons((unsigned short)error);
136313498266Sopenharmony_ci  for(pe = errmsgs; pe->e_code >= 0; pe++)
136413498266Sopenharmony_ci    if(pe->e_code == error)
136513498266Sopenharmony_ci      break;
136613498266Sopenharmony_ci  if(pe->e_code < 0) {
136713498266Sopenharmony_ci    pe->e_msg = strerror(error - 100);
136813498266Sopenharmony_ci    tp->th_code = EUNDEF;   /* set 'undef' errorcode */
136913498266Sopenharmony_ci  }
137013498266Sopenharmony_ci  length = (int)strlen(pe->e_msg);
137113498266Sopenharmony_ci
137213498266Sopenharmony_ci  /* we use memcpy() instead of strcpy() in order to avoid buffer overflow
137313498266Sopenharmony_ci   * report from glibc with FORTIFY_SOURCE */
137413498266Sopenharmony_ci  memcpy(tp->th_msg, pe->e_msg, length + 1);
137513498266Sopenharmony_ci  length += 5;
137613498266Sopenharmony_ci  if(swrite(peer, &buf.storage[0], length) != length)
137713498266Sopenharmony_ci    logmsg("nak: fail\n");
137813498266Sopenharmony_ci}
1379