packet.c 12.1 KB
Newer Older
K
Kozlov Dmitry 已提交
1 2 3 4
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
D
Dmitry Kozlov 已提交
5 6
#include <stdio.h>
#include <unistd.h>
7
#include <fcntl.h>
D
Dmitry Kozlov 已提交
8 9

#include "log.h"
K
Kozlov Dmitry 已提交
10

11
#include "radius_p.h"
K
Kozlov Dmitry 已提交
12

D
Dmitry Kozlov 已提交
13 14
#include "memdebug.h"

15 16 17 18
struct rad_packet_t *rad_packet_alloc(int code)
{
	struct rad_packet_t *pack;

D
Dmitry Kozlov 已提交
19
	pack = _malloc(sizeof(*pack));
20
	if (!pack) {
21
		log_emerg("radius:packet: out of memory\n");
22 23 24 25 26 27 28 29 30 31 32
		return NULL;
	}

	memset(pack, 0, sizeof(*pack));
	pack->code = code;
	pack->len = 20;
	pack->id = 1;
	INIT_LIST_HEAD(&pack->attrs);

	return pack;
}
K
Kozlov Dmitry 已提交
33

34 35 36 37 38 39 40 41 42
void print_buf(uint8_t *buf,int size)
{
	int i;
	for(i=0;i<size;i++)
		printf("%x ",buf[i]);
	printf("\n");
}

int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA)
K
Kozlov Dmitry 已提交
43
{
44
	struct rad_attr_t *attr;
K
Kozlov Dmitry 已提交
45 46
	uint8_t *ptr;

47
	if (pack->buf)
D
Dmitry Kozlov 已提交
48
		ptr = _realloc(pack->buf, pack->len);
49
	else
D
Dmitry Kozlov 已提交
50
		ptr = _malloc(pack->len);
51

K
Kozlov Dmitry 已提交
52
	if (!ptr) {
53
		log_emerg("radius:packet: out of memory\n");
K
Kozlov Dmitry 已提交
54 55 56
		return -1;
	}
	
57
	pack->buf = ptr;
K
Kozlov Dmitry 已提交
58 59
	*ptr = pack->code; ptr++;
	*ptr = pack->id; ptr++;
60 61
	*(uint16_t*)ptr = htons(pack->len); ptr+= 2;
	memcpy(ptr, RA, 16);	ptr+=16;
K
Kozlov Dmitry 已提交
62 63

	list_for_each_entry(attr, &pack->attrs, entry) {
64 65 66 67 68
		if (attr->vendor) {
			*ptr = 26; ptr++;
			*ptr = attr->len + 2 + 6; ptr++;
			*(uint32_t *)ptr = htonl(attr->vendor->id); ptr+=4;
		} 
69 70
		*ptr = attr->attr->id; ptr++;
		*ptr = attr->len + 2; ptr++;
D
Dmitry Kozlov 已提交
71
		switch(attr->attr->type) {
K
Kozlov Dmitry 已提交
72
			case ATTR_TYPE_INTEGER:
73
				*(uint32_t*)ptr = htonl(attr->val.integer);
K
Kozlov Dmitry 已提交
74
				break;
75
			case ATTR_TYPE_OCTETS:
K
Kozlov Dmitry 已提交
76
			case ATTR_TYPE_STRING:
D
Dmitry Kozlov 已提交
77
				memcpy(ptr, attr->val.string, attr->len);
K
Kozlov Dmitry 已提交
78 79 80 81 82
				break;
			case ATTR_TYPE_IPADDR:
				*(in_addr_t*)ptr = attr->val.ipaddr;
				break;
			case ATTR_TYPE_DATE:
83
				*(uint32_t*)ptr = htonl(attr->val.date);
K
Kozlov Dmitry 已提交
84 85
				break;
			default:
86
				log_emerg("radius:packet:BUG: unknown attribute type\n");
K
Kozlov Dmitry 已提交
87 88 89 90 91
				abort();
		}
		ptr += attr->len;
	}

92
	//print_buf(pack->buf, pack->len);
93
	return 0;
K
Kozlov Dmitry 已提交
94 95
}

96
struct rad_packet_t *rad_packet_recv(int fd, struct sockaddr_in *addr)
K
Kozlov Dmitry 已提交
97 98
{
	struct rad_packet_t *pack;
99
	struct rad_attr_t *attr;
K
Kozlov Dmitry 已提交
100
	struct rad_dict_attr_t *da;
101
	struct rad_dict_vendor_t *vendor;
K
Kozlov Dmitry 已提交
102
	uint8_t *ptr;
103
	int n, id, len, vendor_id;
104
	socklen_t addr_len = sizeof(*addr);
K
Kozlov Dmitry 已提交
105

106 107 108
	pack = rad_packet_alloc(0);
	if (!pack)
		return NULL;
K
Kozlov Dmitry 已提交
109

D
Dmitry Kozlov 已提交
110
	pack->buf = _malloc(REQ_LENGTH_MAX);
K
Kozlov Dmitry 已提交
111
	if (!pack->buf) {
112
		log_emerg("radius:packet: out of memory\n");
113
		goto out_err;
K
Kozlov Dmitry 已提交
114 115 116
	}

	while (1) {
117 118 119 120
		if (addr)
			n = recvfrom(fd, pack->buf, REQ_LENGTH_MAX, 0, addr, &addr_len);
		else
			n = read(fd, pack->buf, REQ_LENGTH_MAX);
K
Kozlov Dmitry 已提交
121 122 123
		if (n < 0) {
			if (errno == EINTR)
				continue;
124
			log_ppp_error("radius:packet:read: %s\n", strerror(errno));
K
Kozlov Dmitry 已提交
125 126 127 128 129 130
			goto out_err;
		}
		break;
	}

	if (n < 20) {
131
		log_ppp_warn("radius:packet: short packed received (%i)\n", n);
K
Kozlov Dmitry 已提交
132 133 134 135 136 137 138
		goto out_err;
	}

	ptr = (uint8_t *)pack->buf;

	pack->code = *ptr; ptr++;
	pack->id = *ptr; ptr++;
139
	pack->len = ntohs(*(uint16_t*)ptr); ptr += 2;
K
Kozlov Dmitry 已提交
140 141

	if (pack->len > n) {
142
		log_ppp_warn("radius:packet: short packet received %i, expected %i\n", pack->len, n);
K
Kozlov Dmitry 已提交
143 144 145 146 147 148 149
		goto out_err;
	}

	ptr += 16;
	n -= 20;

	while (n>0) {
150 151 152
		id = *ptr; ptr++;
		len = *ptr - 2; ptr++;
		if (len < 0) {
153
			log_ppp_warn("radius:packet short attribute len received\n");
154 155
			goto out_err;
		}
K
Kozlov Dmitry 已提交
156
		if (2 + len > n) {
157
			log_ppp_warn("radius:packet: too long attribute received (%i, %i)\n", id, len);
K
Kozlov Dmitry 已提交
158 159
			goto out_err;
		}
160 161 162 163 164 165 166 167 168 169
		if (id == 26) {
			vendor_id = ntohl(*(uint32_t *)ptr);
			vendor = rad_dict_find_vendor_id(vendor_id);
			if (vendor) {
				ptr += 4;
				id = *ptr; ptr++;
				len = *ptr - 2; ptr++;
				n -= 2 + len;
			} else
				log_ppp_warn("radius:packet: vendor %s not found\n", id);
170 171
		} else
			vendor = NULL;
172
		da = rad_dict_find_attr_id(vendor, id);
K
Kozlov Dmitry 已提交
173
		if (da) {
D
Dmitry Kozlov 已提交
174
			attr = _malloc(sizeof(*attr));
K
Kozlov Dmitry 已提交
175
			if (!attr) {
176
				log_emerg("radius:packet: out of memory\n");
K
Kozlov Dmitry 已提交
177 178
				goto out_err;
			}
179 180
			memset(attr, 0, sizeof(*attr));
			attr->vendor = vendor;
K
Kozlov Dmitry 已提交
181 182
			attr->attr = da;
			attr->len = len;
183 184
			switch (da->type) {
				case ATTR_TYPE_STRING:
D
Dmitry Kozlov 已提交
185
					attr->val.string = _malloc(len+1);
186
					if (!attr->val.string) {
187
						log_emerg("radius:packet: out of memory\n");
D
Dmitry Kozlov 已提交
188
						_free(attr);
189 190 191 192 193
						goto out_err;
					}
					memcpy(attr->val.string, ptr, len);
					attr->val.string[len] = 0;
					break;
194
				case ATTR_TYPE_OCTETS:
D
Dmitry Kozlov 已提交
195
					attr->val.octets = _malloc(len);
196
					if (!attr->val.octets) {
197
						log_emerg("radius:packet: out of memory\n");
D
Dmitry Kozlov 已提交
198
						_free(attr);
199 200 201 202
						goto out_err;
					}
					memcpy(attr->val.octets, ptr, len);
					break;				
203 204 205 206 207 208 209 210
				case ATTR_TYPE_DATE:
				case ATTR_TYPE_INTEGER:
					attr->val.integer = ntohl(*(uint32_t*)ptr);
					break;
				case ATTR_TYPE_IPADDR:
					attr->val.integer = *(uint32_t*)ptr;
					break;
			}
K
Kozlov Dmitry 已提交
211 212
			list_add_tail(&attr->entry, &pack->attrs);
		} else
213
			log_ppp_warn("radius:packet: unknown attribute received (%i)\n", id);
K
Kozlov Dmitry 已提交
214 215 216 217 218 219 220 221 222 223 224 225 226
		ptr += len;
		n -= 2 + len;
	}

	return pack;

out_err:
	rad_packet_free(pack);
	return NULL;
}

void rad_packet_free(struct rad_packet_t *pack)
{
227
	struct rad_attr_t *attr;
K
Kozlov Dmitry 已提交
228 229
	
	if (pack->buf)
D
Dmitry Kozlov 已提交
230
		_free(pack->buf);
K
Kozlov Dmitry 已提交
231 232 233

	while(!list_empty(&pack->attrs)) {
		attr = list_entry(pack->attrs.next, typeof(*attr), entry);
D
Dmitry Kozlov 已提交
234
		log_ppp_debug("free: %s\n", attr->attr->name);
K
Kozlov Dmitry 已提交
235
		list_del(&attr->entry);
D
Dmitry Kozlov 已提交
236 237 238
		if (attr->attr->type == ATTR_TYPE_STRING || attr->attr->type == ATTR_TYPE_OCTETS)
			_free(attr->val.string);
		_free(attr);
K
Kozlov Dmitry 已提交
239 240
	}

D
Dmitry Kozlov 已提交
241
	_free(pack);
K
Kozlov Dmitry 已提交
242
}
243 244 245

void rad_packet_print(struct rad_packet_t *pack, void (*print)(const char *fmt, ...))
{
246
	struct rad_attr_t *attr;
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
	struct rad_dict_value_t *val;
	
	print("[RADIUS ");
	switch(pack->code) {
		case CODE_ACCESS_REQUEST:
			print("Access-Request");
			break;
		case CODE_ACCESS_CHALLENGE:
			print("Access-Challenge");
			break;
		case CODE_ACCESS_ACCEPT:
			print("Access-Accept");
			break;
		case CODE_ACCESS_REJECT:
			print("Access-Reject");
			break;
263
		case CODE_ACCOUNTING_REQUEST:
264
			print("Accounting-Request");
265 266
			break;
		case CODE_ACCOUNTING_RESPONSE:
267
			print("Accounting-Response");
268
			break;
269
		case CODE_DISCONNECT_REQUEST:
270
			print("Disconnect-Request");
271 272
			break;
		case CODE_DISCONNECT_ACK:
273
			print("Disconnect-ACK");
274 275
			break;
		case CODE_DISCONNECT_NAK:
276
			print("Disconnect-NAK");
277 278
			break;
		case CODE_COA_REQUEST:
279
			print("CoA-Request");
280 281
			break;
		case CODE_COA_ACK:
282
			print("CoA-ACK");
283 284
			break;
		case CODE_COA_NAK:
285
			print("CoA-NAK");
286
			break;
287 288 289 290 291 292
		default:
			print("Unknown (%i)", pack->code);
	}
	print(" id=%x", pack->id);

	list_for_each_entry(attr, &pack->attrs, entry) {
293 294 295 296
		if (attr->vendor)
			print("<%s %s ", attr->vendor->name, attr->attr->name);
		else
			print(" <%s ", attr->attr->name);
297 298 299 300 301 302 303 304 305 306 307 308 309 310
		switch (attr->attr->type) {
			case ATTR_TYPE_INTEGER:
				val = rad_dict_find_val(attr->attr, attr->val);
				if (val)
					print("%s", val->name);
				else
					print("%i", attr->val.integer);
				break;
			case ATTR_TYPE_STRING:
				print("\"%s\"", attr->val.string);
				break;
			case ATTR_TYPE_IPADDR:
				print("%i.%i.%i.%i", attr->val.ipaddr & 0xff, (attr->val.ipaddr >> 8) & 0xff, (attr->val.ipaddr >> 16) & 0xff, (attr->val.ipaddr >> 24) & 0xff);
				break;
311 312 313 314 315 316
		}
		print(">");
	}
	print("]\n");
}

317 318 319 320 321 322 323 324 325 326 327 328
int rad_packet_add_int(struct rad_packet_t *pack, const char *name, int val)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;

	if (pack->len + 2 + 4 >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
D
Dmitry Kozlov 已提交
329
	ra = _malloc(sizeof(*ra));
330 331 332
	if (!ra)
		return -1;

333
	memset(ra, 0, sizeof(*ra));
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
	ra->attr = attr;
	ra->len = 4;
	ra->val.integer = val;
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + 4;

	return 0;
}

int rad_packet_change_int(struct rad_packet_t *pack, const char *name, int val)
{
	struct rad_attr_t *ra;
	
	ra = rad_packet_find_attr(pack, name);
	if (!ra)
		return -1;

	ra->val.integer = val;

	return 0;
}

int rad_packet_add_octets(struct rad_packet_t *pack, const char *name, uint8_t *val, int len)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;

	if (pack->len + 2 + len >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
D
Dmitry Kozlov 已提交
368
	ra = _malloc(sizeof(*ra));
369
	if (!ra) {
370
		log_emerg("radius: out of memory\n");
371 372 373
		return -1;
	}

374
	memset(ra, 0, sizeof(*ra));
375 376
	ra->attr = attr;
	ra->len = len;
D
Dmitry Kozlov 已提交
377
	ra->val.octets = _malloc(len);
378
	if (!ra->val.octets) {
379
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
380
		_free(ra);
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
		return -1;
	}
	memcpy(ra->val.octets, val, len);
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + len;

	return 0;
}
int rad_packet_add_str(struct rad_packet_t *pack, const char *name, const char *val, int len)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;

	if (pack->len + 2 + len >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
D
Dmitry Kozlov 已提交
401
	ra = _malloc(sizeof(*ra));
402
	if (!ra) {
403
		log_emerg("radius: out of memory\n");
404 405 406
		return -1;
	}

407
	memset(ra, 0, sizeof(*ra));
408 409
	ra->attr = attr;
	ra->len = len;
D
Dmitry Kozlov 已提交
410
	ra->val.string = _malloc(len+1);
411
	if (!ra->val.string) {
412
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
413
		_free(ra);
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
		return -1;
	}
	memcpy(ra->val.string, val, len);
	ra->val.string[len] = 0;
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + len;

	return 0;
}

int rad_packet_change_str(struct rad_packet_t *pack, const char *name, const char *val, int len)
{
	struct rad_attr_t *ra;
	
	ra = rad_packet_find_attr(pack, name);
	if (!ra)
		return -1;

	if (ra->len != len) {
		if (pack->len - ra->len + len >= REQ_LENGTH_MAX)
			return -1;

D
Dmitry Kozlov 已提交
436
		ra->val.string = _realloc(ra->val.string, len + 1);
437
		if (!ra->val.string) {
438
			log_emerg("radius: out of memory\n");
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
			return -1;
		}
	
		pack->len += len - ra->len;
		ra->len = len;
	}

	memcpy(ra->val.string, val, len);
	ra->val.string[len] = 0;

	return 0;
}

int rad_packet_add_val(struct rad_packet_t *pack, const char *name, const char *val)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;
	struct rad_dict_value_t *v;

	if (pack->len + 2 + 4 >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
	v = rad_dict_find_val_name(attr, val);
	if (!v)
		return -1;
	
D
Dmitry Kozlov 已提交
469
	ra = _malloc(sizeof(*ra));
470 471 472
	if (!ra)
		return -1;

473
	memset(ra, 0, sizeof(*ra));
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
	ra->attr = attr;
	ra->len = 4;
	ra->val = v->val;
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + 4;

	return 0;
}

int rad_packet_change_val(struct rad_packet_t *pack, const char *name, const char *val)
{
	struct rad_attr_t *ra;
	struct rad_dict_value_t *v;
	
	ra = rad_packet_find_attr(pack, name);
	if (!ra)
		return -1;

	v = rad_dict_find_val_name(ra->attr, val);
	if (!v)
		return -1;

	ra->val = v->val;
	
	return 0;
}

struct rad_attr_t *rad_packet_find_attr(struct rad_packet_t *pack, const char *name)
{
	struct rad_attr_t *ra;

	list_for_each_entry(ra, &pack->attrs, entry)
		if (!strcmp(ra->attr->name, name))
			return ra;

	return NULL;
}

int rad_packet_send(struct rad_packet_t *pack, int fd, struct sockaddr_in *addr)
{
	int n;

	while (1) {
		if (addr)
			n = sendto(fd, pack->buf, pack->len, 0, addr, sizeof(*addr));
		else
			n = write(fd, pack->buf, pack->len);
		if (n < 0) {
			if (errno == EINTR)
				continue;
524
			log_ppp_error("radius:write: %s\n", strerror(errno));
525 526
			return -1;
		} else if (n != pack->len) {
527
			log_ppp_error("radius:write: short write %i, excpected %i\n", n, pack->len);
528 529 530 531 532 533 534 535
			return -1;
		}
		break;
	}

	return 0;
}

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
int rad_packet_add_vendor_octets(struct rad_packet_t *pack, const char *vendor_name, const char *name, const uint8_t *val, int len)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;
	struct rad_dict_vendor_t *vendor;

	if (pack->len + 6 + 2 + len >= REQ_LENGTH_MAX)
		return -1;

	vendor = rad_dict_find_vendor_name(vendor_name);
	if (!vendor)
		return -1;

	attr = rad_dict_find_vendor_attr(vendor, name);
	if (!attr)
		return -1;
	
D
Dmitry Kozlov 已提交
553
	ra = _malloc(sizeof(*ra));
554 555 556 557 558 559 560 561 562
	if (!ra) {
		log_emerg("radius: out of memory\n");
		return -1;
	}
	
	memset(ra, 0, sizeof(*ra));
	ra->vendor = vendor;
	ra->attr = attr;
	ra->len = len;
D
Dmitry Kozlov 已提交
563
	ra->val.octets = _malloc(len);
564 565
	if (!ra->val.octets) {
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
566
		_free(ra);
567 568 569 570 571 572 573 574 575
		return -1;
	}
	memcpy(ra->val.octets, val, len);
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 6 + 2 + len;

	return 0;
}

576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
struct rad_attr_t *rad_packet_find_vendor_attr(struct rad_packet_t *pack, const char *vendor_name, const char *name)
{
	struct rad_attr_t *ra;

	list_for_each_entry(ra, &pack->attrs, entry) {
		if (!ra->vendor)
			continue;
		if (strcmp(ra->vendor->name, vendor_name))
			continue;
		if (strcmp(ra->attr->name, name))
			continue;

		return ra;
	}

	return NULL;
}