The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
encode.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 3ade5916865e76eedd14e69087b1fd0885d1d486 $
19  * @file src/protocols/tftp/encode.c
20  * @brief Functions to encode TFTP packets.
21  * @author Jorge Pereira <jpereira@freeradius.org>
22  *
23  * @copyright 2021 The FreeRADIUS server project.
24  * @copyright 2021 Network RADIUS SAS (legal@networkradius.com)
25  */
26 RCSID("$Id: 3ade5916865e76eedd14e69087b1fd0885d1d486 $")
27 
28 #include <freeradius-devel/util/dbuff.h>
29 #include <freeradius-devel/util/udp.h>
30 
31 #include <freeradius-devel/io/test_point.h>
32 
33 #include "tftp.h"
34 #include "attrs.h"
35 
36 /*
37  * https://tools.ietf.org/html/rfc1350
38  *
39  * Order of Headers
40  *
41  * 2 bytes
42  * ----------------------------------------------------------
43  * | Local Medium | Internet | Datagram | TFTP Opcode |
44  * ----------------------------------------------------------
45  *
46  * TFTP Formats
47  *
48  * Type Op # Format without header
49  *
50  * 2 bytes string 1 byte string 1 byte
51  * -----------------------------------------------
52  * RRQ/ | 01/02 | Filename | 0 | Mode | 0 |
53  * WRQ -----------------------------------------------
54  * 2 bytes 2 bytes n bytes
55  * ---------------------------------
56  * DATA | 03 | Block # | Data |
57  * ---------------------------------
58  * 2 bytes 2 bytes
59  * -------------------
60  * ACK | 04 | Block # |
61  * --------------------
62  * 2 bytes 2 bytes string 1 byte
63  * ----------------------------------------
64  * ERROR | 05 | ErrorCode | ErrMsg | 0 |
65  * ----------------------------------------
66  *
67  * Initial Connection Protocol for reading a file
68  *
69  * 1. Host A sends a "RRQ" to host B with source= A's TID,
70  * destination= 69.
71  *
72  * 2. Host B sends a "DATA" (with block number= 1) to host A with
73  * source= B's TID, destination= A's TID.
74  */
76 {
77  fr_dbuff_t work_dbuff = FR_DBUFF_MAX(dbuff, FR_TFTP_BLOCK_MAX_SIZE);
78  fr_pair_t *vp;
79  uint16_t opcode;
80  char const *buf;
81 
83  if (!vp) {
84  fr_strerror_printf("Cannot send TFTP packet without %s", attr_tftp_opcode->name);
85  return -1;
86  }
87 
88  opcode = vp->vp_uint16;
89  fr_dbuff_in(&work_dbuff, opcode);
90 
91  switch (opcode) {
92  case FR_OPCODE_VALUE_READ_REQUEST:
93  case FR_OPCODE_VALUE_WRITE_REQUEST:
94  /*
95  * 2 bytes string 1 byte string 1 byte string 1 byte string 1 byte
96  * +------------------------------------------------------------------------------------+
97  * | Opcode | Filename | 0 | Mode | 0 | blksize | 0 | #blksize | 0 |
98  * +------------------------------------------------------------------------------------+
99  * Figure 5-1: RRQ/WRQ packet
100  */
101 
102  /* <Filename> */
104  if (!vp) {
105  fr_strerror_printf("Invalid TFTP packet without %s", attr_tftp_filename->name);
106  return -1;
107  }
108 
109  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
110  fr_dbuff_in_bytes(&work_dbuff, '\0');
111 
112  /* <mode> */
113  vp = fr_pair_find_by_da(vps, NULL, attr_tftp_mode);
114  if (!vp) {
115  fr_strerror_printf("Invalid TFTP packet without %s", attr_tftp_mode->name);
116  return -1;
117  }
118 
119  switch(vp->vp_uint16) {
120  case FR_MODE_VALUE_ASCII: buf = "ascii"; break;
121  case FR_MODE_VALUE_OCTET: buf = "octet"; break;
122  default:
123  fr_strerror_printf("Invalid %s value", attr_tftp_mode->name);
124  return -1;
125  }
126 
127  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, buf, 5);
128  fr_dbuff_in_bytes(&work_dbuff, '\0');
129 
130  /* <blksize> is optional */
132  if (vp) {
133  char tmp[5+1]; /* max: 65535 */
134 
135  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, "blksize", 7);
136  fr_dbuff_in_bytes(&work_dbuff, '\0');
137 
138  snprintf(tmp, sizeof(tmp), "%d", vp->vp_uint16); /* #blksize */
139  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, tmp, strlen(tmp));
140  fr_dbuff_in_bytes(&work_dbuff, '\0');
141  }
142 
143  break;
144 
145  case FR_OPCODE_VALUE_ACKNOWLEDGEMENT:
146  case FR_OPCODE_VALUE_DATA:
147  /**
148  * 2 bytes 2 bytes
149  * ---------------------
150  * | Opcode | Block # |
151  * ---------------------
152  * Figure 5-3: ACK packet
153  */
154 
155  /* <Block> */
156  vp = fr_pair_find_by_da(vps, NULL, attr_tftp_block);
157  if (!vp) {
158  fr_strerror_printf("Invalid TFTP packet without %s", attr_tftp_block->name);
159  return -1;
160  }
161 
162  fr_dbuff_in(&work_dbuff, vp->vp_uint16);
163 
164  /*
165  * From that point...
166  *
167  * 2 bytes 2 bytes n bytes
168  * ----------------------------------
169  * | Opcode | Block # | Data |
170  * ----------------------------------
171  * Figure 5-2: DATA packet
172  */
173  if (opcode != FR_OPCODE_VALUE_DATA) goto done;
174 
175  /* <Data> */
176  vp = fr_pair_find_by_da(vps, NULL, attr_tftp_data);
177  if (!vp) {
178  fr_strerror_printf("Invalid TFTP packet without %s", attr_tftp_data->name);
179  return -1;
180  }
181 
182  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_octets, vp->vp_length);
183 
184  break;
185 
186  case FR_OPCODE_VALUE_ERROR:
187  {
188  /**
189  * 2 bytes 2 bytes string 1 byte
190  * -----------------------------------------
191  * | Opcode | ErrorCode | ErrMsg | 0 |
192  * -----------------------------------------
193  *
194  * Figure 5-4: ERROR packet
195  */
196  uint16_t error_code;
197  char const *error_msg;
198  size_t error_msg_len;
199 
200  /* <ErroCode> */
202  if (!vp) {
203  fr_strerror_printf("Invalid TFTP packet without %s", attr_tftp_error_code->name);
204  return -1;
205  }
206 
207  error_code = vp->vp_uint16;
208  fr_dbuff_in(&work_dbuff, error_code);
209 
210  /* <ErrMsg> */
212  if (vp) {
213  error_msg = vp->vp_strvalue;
214  error_msg_len = vp->vp_length;
215  } else {
216  error_msg = fr_tftp_error_codes[error_code] ? fr_tftp_error_codes[error_code] : "Invalid ErrorCode";
217  error_msg_len = strlen(error_msg);
218  }
219 
220  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, error_msg, error_msg_len);
221  fr_dbuff_in_bytes(&work_dbuff, '\0');
222  break;
223  }
224 
225  default:
226  fr_strerror_printf("Invalid TFTP opcode %#04x", opcode);
227  return -1;
228  }
229 
230 done:
231  fr_dbuff_set(dbuff, &work_dbuff);
232 
233  return fr_dbuff_used(dbuff);
234 }
235 
236 /**
237  * Used as the encoder ctx.
238  */
239 typedef struct {
240  int nothing;
241 } fr_tftp_ctx_t;
242 /*
243  * Test points for protocol encode
244  */
245 static ssize_t fr_tftp_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, UNUSED void *proto_ctx)
246 {
247  return fr_tftp_encode(&FR_DBUFF_TMP(data, data_len), vps);
248 }
249 
250 static int _encode_test_ctx(UNUSED fr_tftp_ctx_t *proto_ctx)
251 {
253 
254  return 0;
255 }
256 
257 static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
258 {
259  fr_tftp_ctx_t *test_ctx;
260 
261  if (fr_tftp_global_init() < 0) return -1;
262 
263  test_ctx = talloc_zero(ctx, fr_tftp_ctx_t);
264  if (!test_ctx) return -1;
265 
266  talloc_set_destructor(test_ctx, _encode_test_ctx);
267 
268  *out = test_ctx;
269 
270  return 0;
271 }
272 
273 /*
274  * Test points
275  */
279  .func = fr_tftp_encode_proto
280 };
#define RCSID(id)
Definition: build.h:444
#define UNUSED
Definition: build.h:313
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition: dbuff.h:762
#define fr_dbuff_in_bytes(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker.
Definition: dbuff.h:1460
#define FR_DBUFF_IN_MEMCPY_RETURN(_dbuff_or_marker, _in, _inlen)
Copy exactly _inlen bytes into dbuff or marker returning if there's insufficient space.
Definition: dbuff.h:1377
#define fr_dbuff_in(_dbuff_or_marker, _in)
Copy data from a fixed sized C type into a dbuff or marker.
Definition: dbuff.h:1562
#define FR_DBUFF_MAX(_dbuff_or_marker, _max)
Limit the maximum number of bytes available in the dbuff when passing it to another function.
Definition: dbuff.h:301
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:509
unsigned short uint16_t
Definition: merged_model.c:31
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:688
HIDDEN fr_dict_attr_t const * attr_tftp_error_code
Definition: base.c:47
HIDDEN fr_dict_attr_t const * attr_tftp_filename
Definition: base.c:49
HIDDEN fr_dict_attr_t const * attr_tftp_block_size
Definition: base.c:45
HIDDEN fr_dict_attr_t const * attr_tftp_opcode
Definition: base.c:50
HIDDEN fr_dict_attr_t const * attr_tftp_mode
Definition: base.c:51
HIDDEN fr_dict_attr_t const * attr_tftp_data
Definition: base.c:46
HIDDEN fr_dict_attr_t const * attr_tftp_block
Definition: base.c:44
HIDDEN fr_dict_attr_t const * attr_tftp_error_message
Definition: base.c:48
char const * fr_tftp_error_codes[FR_TFTP_MAX_ERROR_CODE]
Definition: base.c:80
int fr_tftp_global_init(void)
Definition: base.c:90
void fr_tftp_global_free(void)
Definition: base.c:114
static int _encode_test_ctx(UNUSED fr_tftp_ctx_t *proto_ctx)
Definition: encode.c:250
static ssize_t fr_tftp_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, UNUSED void *proto_ctx)
Definition: encode.c:245
ssize_t fr_tftp_encode(fr_dbuff_t *dbuff, fr_pair_list_t *vps)
Definition: encode.c:75
static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
Definition: encode.c:257
fr_test_point_proto_encode_t tftp_tp_encode_proto
Definition: encode.c:277
Used as the decoder ctx.
Definition: decode.c:275
VQP attributes.
static bool done
Definition: radclient.c:80
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition: test_point.h:74
Entry point for protocol encoders.
Definition: test_point.h:73
Functions to encode/decode TFTP packets.
#define FR_TFTP_BLOCK_MAX_SIZE
Definition: tftp.h:73
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
return fr_dbuff_set(dbuff, &our_dbuff)
static fr_slen_t data
Definition: value.h:1259
static size_t char ** out
Definition: value.h:984