qemu/slirp/tftp.c
<<
>>
Prefs
   1/*
   2 * tftp.c - a simple, read-only tftp server for qemu
   3 *
   4 * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include <slirp.h>
  26#include "qemu-common.h"
  27
  28static inline int tftp_session_in_use(struct tftp_session *spt)
  29{
  30    return (spt->slirp != NULL);
  31}
  32
  33static inline void tftp_session_update(struct tftp_session *spt)
  34{
  35    spt->timestamp = curtime;
  36}
  37
  38static void tftp_session_terminate(struct tftp_session *spt)
  39{
  40    if (spt->fd >= 0) {
  41        close(spt->fd);
  42        spt->fd = -1;
  43    }
  44    g_free(spt->filename);
  45    spt->slirp = NULL;
  46}
  47
  48static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
  49{
  50  struct tftp_session *spt;
  51  int k;
  52
  53  for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
  54    spt = &slirp->tftp_sessions[k];
  55
  56    if (!tftp_session_in_use(spt))
  57        goto found;
  58
  59    /* sessions time out after 5 inactive seconds */
  60    if ((int)(curtime - spt->timestamp) > 5000) {
  61        tftp_session_terminate(spt);
  62        goto found;
  63    }
  64  }
  65
  66  return -1;
  67
  68 found:
  69  memset(spt, 0, sizeof(*spt));
  70  memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
  71  spt->fd = -1;
  72  spt->client_port = tp->udp.uh_sport;
  73  spt->slirp = slirp;
  74
  75  tftp_session_update(spt);
  76
  77  return k;
  78}
  79
  80static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
  81{
  82  struct tftp_session *spt;
  83  int k;
  84
  85  for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
  86    spt = &slirp->tftp_sessions[k];
  87
  88    if (tftp_session_in_use(spt)) {
  89      if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
  90        if (spt->client_port == tp->udp.uh_sport) {
  91          return k;
  92        }
  93      }
  94    }
  95  }
  96
  97  return -1;
  98}
  99
 100static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr,
 101                          uint8_t *buf, int len)
 102{
 103    int bytes_read = 0;
 104
 105    if (spt->fd < 0) {
 106        spt->fd = open(spt->filename, O_RDONLY | O_BINARY);
 107    }
 108
 109    if (spt->fd < 0) {
 110        return -1;
 111    }
 112
 113    if (len) {
 114        lseek(spt->fd, block_nr * 512, SEEK_SET);
 115
 116        bytes_read = read(spt->fd, buf, len);
 117    }
 118
 119    return bytes_read;
 120}
 121
 122static int tftp_send_oack(struct tftp_session *spt,
 123                          const char *keys[], uint32_t values[], int nb,
 124                          struct tftp_t *recv_tp)
 125{
 126    struct sockaddr_in saddr, daddr;
 127    struct mbuf *m;
 128    struct tftp_t *tp;
 129    int i, n = 0;
 130
 131    m = m_get(spt->slirp);
 132
 133    if (!m)
 134        return -1;
 135
 136    memset(m->m_data, 0, m->m_size);
 137
 138    m->m_data += IF_MAXLINKHDR;
 139    tp = (void *)m->m_data;
 140    m->m_data += sizeof(struct udpiphdr);
 141
 142    tp->tp_op = htons(TFTP_OACK);
 143    for (i = 0; i < nb; i++) {
 144        n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
 145                      keys[i]) + 1;
 146        n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
 147                      values[i]) + 1;
 148    }
 149
 150    saddr.sin_addr = recv_tp->ip.ip_dst;
 151    saddr.sin_port = recv_tp->udp.uh_dport;
 152
 153    daddr.sin_addr = spt->client_ip;
 154    daddr.sin_port = spt->client_port;
 155
 156    m->m_len = sizeof(struct tftp_t) - 514 + n -
 157        sizeof(struct ip) - sizeof(struct udphdr);
 158    udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
 159
 160    return 0;
 161}
 162
 163static void tftp_send_error(struct tftp_session *spt,
 164                            uint16_t errorcode, const char *msg,
 165                            struct tftp_t *recv_tp)
 166{
 167  struct sockaddr_in saddr, daddr;
 168  struct mbuf *m;
 169  struct tftp_t *tp;
 170
 171  m = m_get(spt->slirp);
 172
 173  if (!m) {
 174    goto out;
 175  }
 176
 177  memset(m->m_data, 0, m->m_size);
 178
 179  m->m_data += IF_MAXLINKHDR;
 180  tp = (void *)m->m_data;
 181  m->m_data += sizeof(struct udpiphdr);
 182
 183  tp->tp_op = htons(TFTP_ERROR);
 184  tp->x.tp_error.tp_error_code = htons(errorcode);
 185  pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
 186
 187  saddr.sin_addr = recv_tp->ip.ip_dst;
 188  saddr.sin_port = recv_tp->udp.uh_dport;
 189
 190  daddr.sin_addr = spt->client_ip;
 191  daddr.sin_port = spt->client_port;
 192
 193  m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
 194        sizeof(struct ip) - sizeof(struct udphdr);
 195
 196  udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
 197
 198out:
 199  tftp_session_terminate(spt);
 200}
 201
 202static void tftp_send_next_block(struct tftp_session *spt,
 203                                 struct tftp_t *recv_tp)
 204{
 205  struct sockaddr_in saddr, daddr;
 206  struct mbuf *m;
 207  struct tftp_t *tp;
 208  int nobytes;
 209
 210  m = m_get(spt->slirp);
 211
 212  if (!m) {
 213    return;
 214  }
 215
 216  memset(m->m_data, 0, m->m_size);
 217
 218  m->m_data += IF_MAXLINKHDR;
 219  tp = (void *)m->m_data;
 220  m->m_data += sizeof(struct udpiphdr);
 221
 222  tp->tp_op = htons(TFTP_DATA);
 223  tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
 224
 225  saddr.sin_addr = recv_tp->ip.ip_dst;
 226  saddr.sin_port = recv_tp->udp.uh_dport;
 227
 228  daddr.sin_addr = spt->client_ip;
 229  daddr.sin_port = spt->client_port;
 230
 231  nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, 512);
 232
 233  if (nobytes < 0) {
 234    m_free(m);
 235
 236    /* send "file not found" error back */
 237
 238    tftp_send_error(spt, 1, "File not found", tp);
 239
 240    return;
 241  }
 242
 243  m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
 244        sizeof(struct ip) - sizeof(struct udphdr);
 245
 246  udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
 247
 248  if (nobytes == 512) {
 249    tftp_session_update(spt);
 250  }
 251  else {
 252    tftp_session_terminate(spt);
 253  }
 254
 255  spt->block_nr++;
 256}
 257
 258static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
 259{
 260  struct tftp_session *spt;
 261  int s, k;
 262  size_t prefix_len;
 263  char *req_fname;
 264  const char *option_name[2];
 265  uint32_t option_value[2];
 266  int nb_options = 0;
 267
 268  /* check if a session already exists and if so terminate it */
 269  s = tftp_session_find(slirp, tp);
 270  if (s >= 0) {
 271    tftp_session_terminate(&slirp->tftp_sessions[s]);
 272  }
 273
 274  s = tftp_session_allocate(slirp, tp);
 275
 276  if (s < 0) {
 277    return;
 278  }
 279
 280  spt = &slirp->tftp_sessions[s];
 281
 282  /* unspecifed prefix means service disabled */
 283  if (!slirp->tftp_prefix) {
 284      tftp_send_error(spt, 2, "Access violation", tp);
 285      return;
 286  }
 287
 288  /* skip header fields */
 289  k = 0;
 290  pktlen -= offsetof(struct tftp_t, x.tp_buf);
 291
 292  /* prepend tftp_prefix */
 293  prefix_len = strlen(slirp->tftp_prefix);
 294  spt->filename = g_malloc(prefix_len + TFTP_FILENAME_MAX + 2);
 295  memcpy(spt->filename, slirp->tftp_prefix, prefix_len);
 296  spt->filename[prefix_len] = '/';
 297
 298  /* get name */
 299  req_fname = spt->filename + prefix_len + 1;
 300
 301  while (1) {
 302    if (k >= TFTP_FILENAME_MAX || k >= pktlen) {
 303      tftp_send_error(spt, 2, "Access violation", tp);
 304      return;
 305    }
 306    req_fname[k] = tp->x.tp_buf[k];
 307    if (req_fname[k++] == '\0') {
 308      break;
 309    }
 310  }
 311
 312  /* check mode */
 313  if ((pktlen - k) < 6) {
 314    tftp_send_error(spt, 2, "Access violation", tp);
 315    return;
 316  }
 317
 318  if (strcasecmp(&tp->x.tp_buf[k], "octet") != 0) {
 319      tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
 320      return;
 321  }
 322
 323  k += 6; /* skipping octet */
 324
 325  /* do sanity checks on the filename */
 326  if (!strncmp(req_fname, "../", 3) ||
 327      req_fname[strlen(req_fname) - 1] == '/' ||
 328      strstr(req_fname, "/../")) {
 329      tftp_send_error(spt, 2, "Access violation", tp);
 330      return;
 331  }
 332
 333  /* check if the file exists */
 334  if (tftp_read_data(spt, 0, NULL, 0) < 0) {
 335      tftp_send_error(spt, 1, "File not found", tp);
 336      return;
 337  }
 338
 339  if (tp->x.tp_buf[pktlen - 1] != 0) {
 340      tftp_send_error(spt, 2, "Access violation", tp);
 341      return;
 342  }
 343
 344  while (k < pktlen && nb_options < ARRAY_SIZE(option_name)) {
 345      const char *key, *value;
 346
 347      key = &tp->x.tp_buf[k];
 348      k += strlen(key) + 1;
 349
 350      if (k >= pktlen) {
 351          tftp_send_error(spt, 2, "Access violation", tp);
 352          return;
 353      }
 354
 355      value = &tp->x.tp_buf[k];
 356      k += strlen(value) + 1;
 357
 358      if (strcasecmp(key, "tsize") == 0) {
 359          int tsize = atoi(value);
 360          struct stat stat_p;
 361
 362          if (tsize == 0) {
 363              if (stat(spt->filename, &stat_p) == 0)
 364                  tsize = stat_p.st_size;
 365              else {
 366                  tftp_send_error(spt, 1, "File not found", tp);
 367                  return;
 368              }
 369          }
 370
 371          option_name[nb_options] = "tsize";
 372          option_value[nb_options] = tsize;
 373          nb_options++;
 374      } else if (strcasecmp(key, "blksize") == 0) {
 375          int blksize = atoi(value);
 376
 377          /* If blksize option is bigger than what we will
 378           * emit, accept the option with our packet size.
 379           * Otherwise, simply do as we didn't see the option.
 380           */
 381          if (blksize >= 512) {
 382              option_name[nb_options] = "blksize";
 383              option_value[nb_options] = 512;
 384              nb_options++;
 385          }
 386      }
 387  }
 388
 389  if (nb_options > 0) {
 390      assert(nb_options <= ARRAY_SIZE(option_name));
 391      tftp_send_oack(spt, option_name, option_value, nb_options, tp);
 392      return;
 393  }
 394
 395  spt->block_nr = 0;
 396  tftp_send_next_block(spt, tp);
 397}
 398
 399static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
 400{
 401  int s;
 402
 403  s = tftp_session_find(slirp, tp);
 404
 405  if (s < 0) {
 406    return;
 407  }
 408
 409  tftp_send_next_block(&slirp->tftp_sessions[s], tp);
 410}
 411
 412static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
 413{
 414  int s;
 415
 416  s = tftp_session_find(slirp, tp);
 417
 418  if (s < 0) {
 419    return;
 420  }
 421
 422  tftp_session_terminate(&slirp->tftp_sessions[s]);
 423}
 424
 425void tftp_input(struct mbuf *m)
 426{
 427  struct tftp_t *tp = (struct tftp_t *)m->m_data;
 428
 429  switch(ntohs(tp->tp_op)) {
 430  case TFTP_RRQ:
 431    tftp_handle_rrq(m->slirp, tp, m->m_len);
 432    break;
 433
 434  case TFTP_ACK:
 435    tftp_handle_ack(m->slirp, tp, m->m_len);
 436    break;
 437
 438  case TFTP_ERROR:
 439    tftp_handle_error(m->slirp, tp, m->m_len);
 440    break;
 441  }
 442}
 443