embox

Форк
0
/
tftp.c 
391 строка · 7.7 Кб
1
/*
2
 * @brief Interface for TFTP operations. Based on old version of cmds/net/tftp.c
3
 * @author Denis Deryugin <deryugin.denis@gmail.com>
4
 * @author Nikolay Korotky
5
 * @author Ilia Vaprol
6
 * @version
7
 * @date 18.06.2019
8
 */
9

10
#include <assert.h>
11
#include <errno.h>
12
#include <stdio.h>
13
#include <stdlib.h>
14
#include <string.h>
15
#include <unistd.h>
16
#include <stddef.h>
17
#include <sys/socket.h>
18
#include <arpa/inet.h>
19

20
#include <lib/tftp.h>
21
#include <util/log.h>
22
#include <kernel/printk.h>
23

24
struct tftp_msg {
25
	uint16_t opcode;
26
	union {
27
		struct {
28
			char name_and_mode[2];
29
		} cmd /*__attribute__ ((packed))*/;
30
		struct {
31
			uint16_t block_num;
32
			char stuff[TFTP_SEGSIZE];
33
		} data /*__attribute__ ((packed))*/;
34
		struct {
35
			uint16_t block_num;
36
		} ack /*__attribute__ ((packed))*/;
37
		struct {
38
			uint16_t error_code;
39
			char error_msg[1];
40
		} err /*__attriibute__ ((packed))*/;
41
	} op /*__attribute__ ((packed))*/;
42
} __attribute__ ((packed));
43

44
struct tftp_stream {
45
	char filename[PATH_MAX];
46

47
	int pkg_number;
48

49
	struct tftp_msg snd;
50
	struct tftp_msg rcv;
51
	size_t snd_len;
52
	size_t rcv_len;
53

54
	struct sockaddr_storage rem_addr;
55
	socklen_t rem_addrlen;
56

57
	int sock;
58

59
	bool transmission_end;
60
};
61

62
static char *get_transfer_mode(bool binary_on) {
63
	return binary_on ? "octet" : "netascii";
64
}
65

66
static int tftp_build_msg_cmd(struct tftp_msg *msg, size_t *msg_len,
67
		uint16_t type, const char *filename, char *mode) {
68
	char *ptr;
69
	size_t sz;
70

71
	msg->opcode = htons(type);
72
	*msg_len = sizeof msg->opcode;
73

74
	ptr = &msg->op.cmd.name_and_mode[0];
75
	sz = strlen(filename) + 1;
76
	memcpy(ptr, filename, sz * sizeof(char));
77
	*msg_len += sz * sizeof(char);
78

79
	ptr += sz;
80
	sz = strlen(mode) + 1;
81
	memcpy(ptr, mode, sz * sizeof(char));
82
	*msg_len += sz * sizeof(char);
83

84
	return 0;
85
}
86

87
static int tftp_build_msg_data(struct tftp_msg *msg, size_t *msg_len,
88
		uint8_t *data, int data_len, uint16_t block_num) {
89
	msg->opcode = htons(TFTP_DATA);
90
	*msg_len = sizeof msg->opcode;
91

92
	msg->op.data.block_num = htons(block_num);
93
	*msg_len += sizeof msg->op.data.block_num;
94

95
	memcpy(msg->op.data.stuff, data, data_len);
96

97
	*msg_len += data_len;
98

99
	return 0;
100
}
101

102
static int tftp_build_msg_ack(struct tftp_msg *msg, size_t *msg_len, uint16_t block_num) {
103
	msg->opcode = htons(TFTP_ACK);
104
	*msg_len = sizeof msg->opcode;
105

106
	msg->op.ack.block_num = htons(block_num);
107
	*msg_len += sizeof msg->op.ack.block_num;
108

109
	return 0;
110
}
111

112
static int msg_with_correct_len(struct tftp_msg *msg, size_t msg_len) {
113
	size_t field_sz, left_sz;
114
	char *tmp;
115

116
	left_sz = msg_len;
117

118
	field_sz = sizeof msg->opcode;
119
	if (left_sz < field_sz) return 0;
120

121
	left_sz -= field_sz;
122

123
	switch (ntohs(msg->opcode)) {
124
	case TFTP_RRQ:
125
	case TFTP_WRQ:
126
		tmp = &msg->op.cmd.name_and_mode[0];
127
		/* filename */
128
		do
129
			if (left_sz-- == 0) return 0;
130
		while (*tmp++ != '\0');
131
		/* mode */
132
		do
133
			if (left_sz-- == 0) return 0;
134
		while (*tmp++ != '\0');
135
		break;
136
	case TFTP_DATA:
137
		/* block number */
138
		field_sz = sizeof msg->op.data.block_num;
139
		if (left_sz < field_sz) return 0;
140
		left_sz -= field_sz;
141
		/* data */
142
		left_sz = 0;
143
		break;
144
	case TFTP_ACK:
145
		/* block number */
146
		field_sz = sizeof msg->op.ack.block_num;
147
		if (left_sz < field_sz) return 0;
148
		left_sz -= field_sz;
149
		break;
150
	case TFTP_ERROR:
151
		/* error code */
152
		field_sz = sizeof msg->op.err.error_code;
153
		if (left_sz < field_sz) return 0;
154
		left_sz -= field_sz;
155
		/* error message */
156
		tmp = &msg->op.err.error_msg[0];
157
		do
158
			if (left_sz-- == 0) return 0;
159
		while (*tmp++ != '\0');
160
		break;
161
	default: /* unknown operation */
162
		return 0;
163
	}
164

165
	return !left_sz;
166
}
167

168
static int 
169
tftp_msg_send(struct tftp_msg *msg, size_t msg_len, struct tftp_stream *s) {
170
	int res;
171

172
	assert(s);
173
	assert(msg);
174
	 /* debug msg_with_correct_len */
175
	assert(msg_with_correct_len(msg, msg_len));
176

177
	res = sendto(s->sock, (char *)msg, msg_len, 0,
178
					(struct sockaddr *) &s->rem_addr, s->rem_addrlen);
179
	if (res == -1) {
180
		log_error("tftp: send() failure");
181
		return -errno;
182
	}
183

184
	return 0;
185
}
186

187
static int 
188
tftp_msg_recv(struct tftp_msg *msg, size_t *msg_len, struct tftp_stream *s) {
189
	ssize_t ret;
190

191
	assert(s);
192
	assert(msg);
193
	assert(msg_len);
194

195
	ret = recvfrom(s->sock, (char *) msg, sizeof (*msg), 0,
196
						(struct sockaddr *)&s->rem_addr, &s->rem_addrlen);
197
	if (ret == -1) {
198
		log_error("tftp: recv() failure");
199
		return -errno;
200
	}
201

202
	*msg_len = ret;
203

204
	return 0;
205
}
206

207
static struct tftp_stream stream;
208

209
static int make_remote_addr(const char *hostname,
210
		struct sockaddr_storage *out_raddr, socklen_t *out_raddr_len) {
211
	int ret;
212
	struct sockaddr_in *raddr_in;
213

214
	raddr_in = (struct sockaddr_in *)out_raddr;
215
	memset(out_raddr, 0, sizeof *out_raddr);
216
	raddr_in->sin_family = AF_INET;
217
	raddr_in->sin_port = htons(TFTP_TRANSFER_PORT);
218
	ret = inet_aton(hostname, &raddr_in->sin_addr);
219
	if (!ret) {
220
		fprintf(stderr, "Can't parse remote address '%s`\n", hostname);
221
		return -EINVAL;
222
	}
223

224
	*out_raddr_len = sizeof *raddr_in;
225

226
	return 0;
227
}
228

229
static int tftp_resend_cmd(struct tftp_stream *s) {
230
	if (0 != tftp_msg_send(&s->snd, s->snd_len, s)) {
231
		return -1;
232
	}
233

234
	if (0 != tftp_msg_recv(&s->rcv, &s->rcv_len, s)) {
235
		return -2;
236
	}
237

238
	return 0;
239
}
240

241
struct tftp_stream *tftp_new_stream(const char *host, const char *file, int dir, bool binary_mode) {
242
	struct tftp_stream *s = &stream;
243

244
	memset(s, 0, sizeof(*s));
245

246
	if (0 != make_remote_addr(host,
247
				&s->rem_addr,
248
				&s->rem_addrlen)) {
249
		goto fail;
250
	}
251
	if (-1 == (s->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))) {
252
		goto fail;
253
	}
254

255
	if (dir == TFTP_DIR_PUT) {
256
		if (0 != tftp_build_msg_cmd(&s->snd,
257
				&s->snd_len,
258
				TFTP_WRQ,
259
				file,
260
				get_transfer_mode(binary_mode))) {
261
			goto fail;
262
		}
263
	} else {
264
		if (0 != tftp_build_msg_cmd(&s->snd,
265
				&s->snd_len,
266
				TFTP_RRQ,
267
				file,
268
				get_transfer_mode(binary_mode))) {
269
			goto fail;
270
		}
271
	}
272

273
	do {
274
		if (tftp_resend_cmd(s)) {
275
			log_error("ERROR\n");
276
			break;
277
		}
278
	} while (!msg_with_correct_len(&s->rcv, s->rcv_len));
279

280
	strncpy(s->filename, file, sizeof(s->filename));
281

282
	return s;
283

284
fail:
285
	if (s->sock >= 0) {
286
		close(s->sock);
287
	}
288

289
	memset(s, 0, sizeof(*s));
290

291
	return NULL;
292
}
293

294
int tftp_delete_stream(struct tftp_stream *s) {
295
	if (s == NULL) {
296
		return -EINVAL;
297
	}
298

299
	if (s->sock >= 0) {
300
		close(s->sock);
301
	}
302

303
	memset(s, 0, sizeof(*s));
304

305
	return 0;
306
}
307

308
int tftp_stream_write(struct tftp_stream *s, uint8_t *buf, size_t len) {
309
	if (s == NULL) {
310
		log_warning("stream is NULL, do nothing");
311
		return 0;
312
	}
313

314
	switch (ntohs(s->rcv.opcode)) {
315
		case TFTP_ACK:
316
			while (ntohs(s->rcv.op.ack.block_num) != s->pkg_number) {
317
				tftp_resend_cmd(s); /* invalid acknowledgement, send again */
318
			}
319
			break;
320
		case TFTP_ERROR:
321
			return -s->rcv.op.err.error_code;
322
		default:
323
			/* error */
324
			return 0;
325
	}
326

327
	/* whether we have more data to transfer? */
328
	if ((s->pkg_number != 0) && (s->snd_len != sizeof s->snd)) {
329
		/* end of transmission */
330
	}
331

332
	/* get next stuff of data */
333
	if (0 != tftp_build_msg_data(&s->snd, &s->snd_len, buf, len, ++s->pkg_number)) {
334
		goto fail;
335
	}
336

337
	/* send request / data */
338
	if (0 != tftp_msg_send(&s->snd, s->snd_len, s)) {
339
		goto fail;
340
	}
341

342
	return s->snd_len;
343
fail:
344
	return 0;
345
}
346

347
int tftp_stream_read(struct tftp_stream *s, uint8_t *buf) {
348
	int data_len;
349

350
	if (s->transmission_end) {
351
		return 0;
352
	}
353

354
	switch (ntohs(s->rcv.opcode)) {
355
		case TFTP_DATA:
356
			while (ntohs(s->rcv.op.ack.block_num) != s->pkg_number + 1) {
357
				tftp_resend_cmd(s);
358
			}
359

360
			data_len = s->rcv_len - (sizeof s->rcv - sizeof s->rcv.op.data.stuff);
361
			memcpy(buf, s->rcv.op.data.stuff, data_len);
362

363
			if (s->rcv_len != sizeof(s->rcv)) {
364
				s->transmission_end = true;
365
			}
366
			break;
367
		case TFTP_ERROR:
368
			return -s->rcv.op.err.error_code;
369
			goto fail;
370
		default:
371
			goto fail;
372
	}
373

374
	if (0 != tftp_build_msg_ack(&s->snd, &s->snd_len, ++s->pkg_number)) {
375
		goto fail;
376
	}
377

378
	if (0 != tftp_msg_send(&s->snd, s->snd_len, s)) {
379
		goto fail;
380
	}
381

382
	return data_len;
383
fail:
384
	return 0;
385
}
386

387
const char *tftp_error(struct tftp_stream *s) {
388
	assert(s);
389

390
	return s->rcv.op.err.error_msg;
391
}
392

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.