52#include <limits.h> 53#ifdef HAVE_ERR_H 54#include <err.h> 55#endif 56 57#include "support.h" 58#include "asn1.h" 59#include "snmp.h" 60#include "snmpclient.h" 61#include "snmppriv.h" 62 63/* global context */ 64struct snmp_client snmp_client; 65 66/* List of all outstanding requests */ 67struct sent_pdu { 68 int reqid; 69 struct snmp_pdu *pdu; 70 struct timeval time; 71 u_int retrycount; 72 snmp_send_cb_f callback; 73 void *arg; 74 void *timeout_id; 75 LIST_ENTRY(sent_pdu) entries; 76}; 77LIST_HEAD(sent_pdu_list, sent_pdu); 78 79static struct sent_pdu_list sent_pdus; 80 81/* 82 * Prototype table entry. All C-structure produced by the table function must 83 * start with these two fields. This relies on the fact, that all TAILQ_ENTRY 84 * are compatible with each other in the sense implied by ANSI-C. 85 */ 86struct entry { 87 TAILQ_ENTRY(entry) link; 88 uint64_t found; 89}; 90TAILQ_HEAD(table, entry); 91 92/* 93 * working list entry. This list is used to hold the Index part of the 94 * table row's. The entry list and the work list parallel each other. 95 */ 96struct work { 97 TAILQ_ENTRY(work) link; 98 struct asn_oid index; 99}; 100TAILQ_HEAD(worklist, work); 101 102/* 103 * Table working data 104 */ 105struct tabwork { 106 const struct snmp_table *descr; 107 struct table *table; 108 struct worklist worklist; 109 uint32_t last_change; 110 int first; 111 u_int iter; 112 snmp_table_cb_f callback; 113 void *arg; 114 struct snmp_pdu pdu; 115}; 116 117/* 118 * Set the error string 119 */ 120static void 121seterr(struct snmp_client *sc, const char *fmt, ...) 122{ 123 va_list ap; 124 125 va_start(ap, fmt); 126 vsnprintf(sc->error, sizeof(sc->error), fmt, ap); 127 va_end(ap); 128} 129 130/* 131 * Free the entire table and work list. If table is NULL only the worklist 132 * is freed. 133 */ 134static void 135table_free(struct tabwork *work, int all) 136{ 137 struct work *w; 138 struct entry *e; 139 const struct snmp_table_entry *d; 140 u_int i; 141 142 while ((w = TAILQ_FIRST(&work->worklist)) != NULL) { 143 TAILQ_REMOVE(&work->worklist, w, link); 144 free(w); 145 } 146 147 if (all == 0) 148 return; 149 150 while ((e = TAILQ_FIRST(work->table)) != NULL) { 151 for (i = 0; work->descr->entries[i].syntax != SNMP_SYNTAX_NULL; 152 i++) { 153 d = &work->descr->entries[i]; 154 if (d->syntax == SNMP_SYNTAX_OCTETSTRING && 155 (e->found & ((uint64_t)1 << i))) 156 free(*(void **)(void *) 157 ((u_char *)e + d->offset)); 158 } 159 TAILQ_REMOVE(work->table, e, link); 160 free(e); 161 } 162} 163 164/* 165 * Find the correct table entry for the given variable. If non exists, 166 * create one. 167 */ 168static struct entry * 169table_find(struct tabwork *work, const struct asn_oid *var) 170{ 171 struct entry *e, *e1; 172 struct work *w, *w1; 173 u_int i, p, j; 174 size_t len; 175 u_char *ptr; 176 struct asn_oid oid; 177 178 /* get index */ 179 asn_slice_oid(&oid, var, work->descr->table.len + 2, var->len); 180 181 e = TAILQ_FIRST(work->table); 182 w = TAILQ_FIRST(&work->worklist); 183 while (e != NULL) { 184 if (asn_compare_oid(&w->index, &oid) == 0) 185 return (e); 186 e = TAILQ_NEXT(e, link); 187 w = TAILQ_NEXT(w, link); 188 } 189 190 /* Not found create new one */ 191 if ((e = malloc(work->descr->entry_size)) == NULL) { 192 seterr(&snmp_client, "no memory for table entry"); 193 return (NULL); 194 } 195 if ((w = malloc(sizeof(*w))) == NULL) { 196 seterr(&snmp_client, "no memory for table entry"); 197 free(e); 198 return (NULL); 199 } 200 w->index = oid; 201 memset(e, 0, work->descr->entry_size); 202 203 /* decode index */ 204 p = work->descr->table.len + 2; 205 for (i = 0; i < work->descr->index_size; i++) { 206 switch (work->descr->entries[i].syntax) { 207 208 case SNMP_SYNTAX_INTEGER: 209 if (var->len < p + 1) { 210 seterr(&snmp_client, "bad index: need integer"); 211 goto err; 212 } 213 if (var->subs[p] > INT32_MAX) { 214 seterr(&snmp_client, 215 "bad index: integer too large"); 216 goto err; 217 } 218 *(int32_t *)(void *)((u_char *)e + 219 work->descr->entries[i].offset) = var->subs[p++]; 220 break; 221 222 case SNMP_SYNTAX_OCTETSTRING: 223 if (var->len < p + 1) { 224 seterr(&snmp_client, 225 "bad index: need string length"); 226 goto err; 227 } 228 len = var->subs[p++]; 229 if (var->len < p + len) { 230 seterr(&snmp_client, 231 "bad index: string too short"); 232 goto err; 233 } 234 if ((ptr = malloc(len + 1)) == NULL) { 235 seterr(&snmp_client, 236 "no memory for index string"); 237 goto err; 238 } 239 for (j = 0; j < len; j++) { 240 if (var->subs[p] > UCHAR_MAX) { 241 seterr(&snmp_client, 242 "bad index: char too large"); 243 free(ptr); 244 goto err; 245 } 246 ptr[j] = var->subs[p++]; 247 } 248 ptr[j] = '\0'; 249 *(u_char **)(void *)((u_char *)e + 250 work->descr->entries[i].offset) = ptr; 251 *(size_t *)(void *)((u_char *)e + 252 work->descr->entries[i].offset + sizeof(u_char *)) 253 = len; 254 break; 255 256 case SNMP_SYNTAX_OID: 257 if (var->len < p + 1) { 258 seterr(&snmp_client, 259 "bad index: need oid length"); 260 goto err; 261 } 262 oid.len = var->subs[p++]; 263 if (var->len < p + oid.len) { 264 seterr(&snmp_client, 265 "bad index: oid too short"); 266 goto err; 267 } 268 for (j = 0; j < oid.len; j++) 269 oid.subs[j] = var->subs[p++]; 270 *(struct asn_oid *)(void *)((u_char *)e + 271 work->descr->entries[i].offset) = oid; 272 break; 273 274 case SNMP_SYNTAX_IPADDRESS: 275 if (var->len < p + 4) { 276 seterr(&snmp_client, 277 "bad index: need ip-address"); 278 goto err; 279 } 280 for (j = 0; j < 4; j++) { 281 if (var->subs[p] > 0xff) { 282 seterr(&snmp_client, 283 "bad index: ipaddress too large"); 284 goto err; 285 } 286 ((u_char *)e + 287 work->descr->entries[i].offset)[j] = 288 var->subs[p++]; 289 } 290 break; 291 292 case SNMP_SYNTAX_GAUGE: 293 if (var->len < p + 1) { 294 seterr(&snmp_client, 295 "bad index: need unsigned"); 296 goto err; 297 } 298 if (var->subs[p] > UINT32_MAX) { 299 seterr(&snmp_client, 300 "bad index: unsigned too large"); 301 goto err; 302 } 303 *(uint32_t *)(void *)((u_char *)e + 304 work->descr->entries[i].offset) = var->subs[p++]; 305 break; 306 307 case SNMP_SYNTAX_COUNTER: 308 case SNMP_SYNTAX_TIMETICKS: 309 case SNMP_SYNTAX_COUNTER64: 310 case SNMP_SYNTAX_NULL: 311 case SNMP_SYNTAX_NOSUCHOBJECT: 312 case SNMP_SYNTAX_NOSUCHINSTANCE: 313 case SNMP_SYNTAX_ENDOFMIBVIEW: 314 abort(); 315 } 316 e->found |= (uint64_t)1 << i; 317 } 318 319 /* link into the correct place */ 320 e1 = TAILQ_FIRST(work->table); 321 w1 = TAILQ_FIRST(&work->worklist); 322 while (e1 != NULL) { 323 if (asn_compare_oid(&w1->index, &w->index) > 0) 324 break; 325 e1 = TAILQ_NEXT(e1, link); 326 w1 = TAILQ_NEXT(w1, link); 327 } 328 if (e1 == NULL) { 329 TAILQ_INSERT_TAIL(work->table, e, link); 330 TAILQ_INSERT_TAIL(&work->worklist, w, link); 331 } else { 332 TAILQ_INSERT_BEFORE(e1, e, link); 333 TAILQ_INSERT_BEFORE(w1, w, link); 334 } 335 336 return (e); 337 338 err: 339 /* 340 * Error happend. Free all octet string index parts and the entry 341 * itself. 342 */ 343 for (i = 0; i < work->descr->index_size; i++) { 344 if (work->descr->entries[i].syntax == SNMP_SYNTAX_OCTETSTRING && 345 (e->found & ((uint64_t)1 << i))) 346 free(*(void **)(void *)((u_char *)e + 347 work->descr->entries[i].offset)); 348 } 349 free(e); 350 free(w); 351 return (NULL); 352} 353 354/* 355 * Assign the value 356 */ 357static int 358table_value(const struct snmp_table *descr, struct entry *e, 359 const struct snmp_value *b) 360{ 361 u_int i; 362 u_char *ptr; 363 364 for (i = descr->index_size; 365 descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++) 366 if (descr->entries[i].subid == 367 b->var.subs[descr->table.len + 1]) 368 break; 369 if (descr->entries[i].syntax == SNMP_SYNTAX_NULL) 370 return (0); 371 372 /* check syntax */ 373 if (b->syntax != descr->entries[i].syntax) { 374 seterr(&snmp_client, "bad syntax (%u instead of %u)", b->syntax, 375 descr->entries[i].syntax); 376 return (-1); 377 } 378 379 switch (b->syntax) { 380 381 case SNMP_SYNTAX_INTEGER: 382 *(int32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 383 b->v.integer; 384 break; 385 386 case SNMP_SYNTAX_OCTETSTRING: 387 if ((ptr = malloc(b->v.octetstring.len + 1)) == NULL) { 388 seterr(&snmp_client, "no memory for string"); 389 return (-1); 390 } 391 memcpy(ptr, b->v.octetstring.octets, b->v.octetstring.len); 392 ptr[b->v.octetstring.len] = '\0'; 393 *(u_char **)(void *)((u_char *)e + descr->entries[i].offset) = 394 ptr; 395 *(size_t *)(void *)((u_char *)e + descr->entries[i].offset + 396 sizeof(u_char *)) = b->v.octetstring.len; 397 break; 398 399 case SNMP_SYNTAX_OID: 400 *(struct asn_oid *)(void *)((u_char *)e + descr->entries[i].offset) = 401 b->v.oid; 402 break; 403 404 case SNMP_SYNTAX_IPADDRESS: 405 memcpy((u_char *)e + descr->entries[i].offset, 406 b->v.ipaddress, 4); 407 break; 408 409 case SNMP_SYNTAX_COUNTER: 410 case SNMP_SYNTAX_GAUGE: 411 case SNMP_SYNTAX_TIMETICKS: 412 *(uint32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 413 b->v.uint32; 414 break; 415 416 case SNMP_SYNTAX_COUNTER64: 417 *(uint64_t *)(void *)((u_char *)e + descr->entries[i].offset) = 418 b->v.counter64; 419 break; 420 421 case SNMP_SYNTAX_NULL: 422 case SNMP_SYNTAX_NOSUCHOBJECT: 423 case SNMP_SYNTAX_NOSUCHINSTANCE: 424 case SNMP_SYNTAX_ENDOFMIBVIEW: 425 abort(); 426 } 427 e->found |= (uint64_t)1 << i; 428 429 return (0); 430} 431 432/* 433 * Initialize the first PDU to send 434 */ 435static void 436table_init_pdu(const struct snmp_table *descr, struct snmp_pdu *pdu) 437{ 438 if (snmp_client.version == SNMP_V1) 439 snmp_pdu_create(pdu, SNMP_PDU_GETNEXT); 440 else { 441 snmp_pdu_create(pdu, SNMP_PDU_GETBULK); 442 pdu->error_index = 10; 443 } 444 if (descr->last_change.len != 0) { 445 pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 446 pdu->bindings[pdu->nbindings].var = descr->last_change; 447 pdu->nbindings++; 448 if (pdu->version != SNMP_V1) 449 pdu->error_status++; 450 } 451 pdu->bindings[pdu->nbindings].var = descr->table; 452 pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 453 pdu->nbindings++; 454} 455 456/* 457 * Return code: 458 * 0 - End Of Table 459 * -1 - Error 460 * -2 - Last change changed - again 461 * +1 - ok, continue 462 */ 463static int 464table_check_response(struct tabwork *work, const struct snmp_pdu *resp) 465{ 466 const struct snmp_value *b; 467 struct entry *e; 468 469 if (resp->error_status != SNMP_ERR_NOERROR) { 470 if (snmp_client.version == SNMP_V1 && 471 resp->error_status == SNMP_ERR_NOSUCHNAME && 472 resp->error_index == 473 (work->descr->last_change.len == 0) ? 1 : 2) 474 /* EOT */ 475 return (0); 476 /* Error */ 477 seterr(&snmp_client, "error fetching table: status=%d index=%d", 478 resp->error_status, resp->error_index); 479 return (-1); 480 } 481 482 for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) { 483 if (work->descr->last_change.len != 0 && b == resp->bindings) { 484 if (!asn_is_suboid(&work->descr->last_change, &b->var) || 485 b->var.len != work->descr->last_change.len + 1 || 486 b->var.subs[work->descr->last_change.len] != 0) { 487 seterr(&snmp_client, 488 "last_change: bad response"); 489 return (-1); 490 } 491 if (b->syntax != SNMP_SYNTAX_TIMETICKS) { 492 seterr(&snmp_client, 493 "last_change: bad syntax %u", b->syntax); 494 return (-1); 495 } 496 if (work->first) { 497 work->last_change = b->v.uint32; 498 work->first = 0; 499 500 } else if (work->last_change != b->v.uint32) { 501 if (++work->iter >= work->descr->max_iter) { 502 seterr(&snmp_client, 503 "max iteration count exceeded"); 504 return (-1); 505 } 506 table_free(work, 1); 507 return (-2); 508 } 509 510 continue; 511 } 512 if (!asn_is_suboid(&work->descr->table, &b->var) || 513 b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 514 return (0); 515 516 if ((e = table_find(work, &b->var)) == NULL) 517 return (-1); 518 if (table_value(work->descr, e, b)) 519 return (-1); 520 } 521 return (+1); 522} 523 524/* 525 * Check table consistency 526 */ 527static int 528table_check_cons(struct tabwork *work) 529{ 530 struct entry *e; 531 532 TAILQ_FOREACH(e, work->table, link) 533 if ((e->found & work->descr->req_mask) != 534 work->descr->req_mask) { 535 if (work->descr->last_change.len == 0) { 536 if (++work->iter >= work->descr->max_iter) { 537 seterr(&snmp_client, 538 "max iteration count exceeded"); 539 return (-1); 540 } 541 return (-2); 542 } 543 seterr(&snmp_client, "inconsistency detected %llx %llx", 544 e->found, work->descr->req_mask); 545 return (-1); 546 } 547 return (0); 548} 549 550/* 551 * Fetch a table. Returns 0 if ok, -1 on errors.
| 56#include <limits.h> 57#ifdef HAVE_ERR_H 58#include <err.h> 59#endif 60 61#include "support.h" 62#include "asn1.h" 63#include "snmp.h" 64#include "snmpclient.h" 65#include "snmppriv.h" 66 67/* global context */ 68struct snmp_client snmp_client; 69 70/* List of all outstanding requests */ 71struct sent_pdu { 72 int reqid; 73 struct snmp_pdu *pdu; 74 struct timeval time; 75 u_int retrycount; 76 snmp_send_cb_f callback; 77 void *arg; 78 void *timeout_id; 79 LIST_ENTRY(sent_pdu) entries; 80}; 81LIST_HEAD(sent_pdu_list, sent_pdu); 82 83static struct sent_pdu_list sent_pdus; 84 85/* 86 * Prototype table entry. All C-structure produced by the table function must 87 * start with these two fields. This relies on the fact, that all TAILQ_ENTRY 88 * are compatible with each other in the sense implied by ANSI-C. 89 */ 90struct entry { 91 TAILQ_ENTRY(entry) link; 92 uint64_t found; 93}; 94TAILQ_HEAD(table, entry); 95 96/* 97 * working list entry. This list is used to hold the Index part of the 98 * table row's. The entry list and the work list parallel each other. 99 */ 100struct work { 101 TAILQ_ENTRY(work) link; 102 struct asn_oid index; 103}; 104TAILQ_HEAD(worklist, work); 105 106/* 107 * Table working data 108 */ 109struct tabwork { 110 const struct snmp_table *descr; 111 struct table *table; 112 struct worklist worklist; 113 uint32_t last_change; 114 int first; 115 u_int iter; 116 snmp_table_cb_f callback; 117 void *arg; 118 struct snmp_pdu pdu; 119}; 120 121/* 122 * Set the error string 123 */ 124static void 125seterr(struct snmp_client *sc, const char *fmt, ...) 126{ 127 va_list ap; 128 129 va_start(ap, fmt); 130 vsnprintf(sc->error, sizeof(sc->error), fmt, ap); 131 va_end(ap); 132} 133 134/* 135 * Free the entire table and work list. If table is NULL only the worklist 136 * is freed. 137 */ 138static void 139table_free(struct tabwork *work, int all) 140{ 141 struct work *w; 142 struct entry *e; 143 const struct snmp_table_entry *d; 144 u_int i; 145 146 while ((w = TAILQ_FIRST(&work->worklist)) != NULL) { 147 TAILQ_REMOVE(&work->worklist, w, link); 148 free(w); 149 } 150 151 if (all == 0) 152 return; 153 154 while ((e = TAILQ_FIRST(work->table)) != NULL) { 155 for (i = 0; work->descr->entries[i].syntax != SNMP_SYNTAX_NULL; 156 i++) { 157 d = &work->descr->entries[i]; 158 if (d->syntax == SNMP_SYNTAX_OCTETSTRING && 159 (e->found & ((uint64_t)1 << i))) 160 free(*(void **)(void *) 161 ((u_char *)e + d->offset)); 162 } 163 TAILQ_REMOVE(work->table, e, link); 164 free(e); 165 } 166} 167 168/* 169 * Find the correct table entry for the given variable. If non exists, 170 * create one. 171 */ 172static struct entry * 173table_find(struct tabwork *work, const struct asn_oid *var) 174{ 175 struct entry *e, *e1; 176 struct work *w, *w1; 177 u_int i, p, j; 178 size_t len; 179 u_char *ptr; 180 struct asn_oid oid; 181 182 /* get index */ 183 asn_slice_oid(&oid, var, work->descr->table.len + 2, var->len); 184 185 e = TAILQ_FIRST(work->table); 186 w = TAILQ_FIRST(&work->worklist); 187 while (e != NULL) { 188 if (asn_compare_oid(&w->index, &oid) == 0) 189 return (e); 190 e = TAILQ_NEXT(e, link); 191 w = TAILQ_NEXT(w, link); 192 } 193 194 /* Not found create new one */ 195 if ((e = malloc(work->descr->entry_size)) == NULL) { 196 seterr(&snmp_client, "no memory for table entry"); 197 return (NULL); 198 } 199 if ((w = malloc(sizeof(*w))) == NULL) { 200 seterr(&snmp_client, "no memory for table entry"); 201 free(e); 202 return (NULL); 203 } 204 w->index = oid; 205 memset(e, 0, work->descr->entry_size); 206 207 /* decode index */ 208 p = work->descr->table.len + 2; 209 for (i = 0; i < work->descr->index_size; i++) { 210 switch (work->descr->entries[i].syntax) { 211 212 case SNMP_SYNTAX_INTEGER: 213 if (var->len < p + 1) { 214 seterr(&snmp_client, "bad index: need integer"); 215 goto err; 216 } 217 if (var->subs[p] > INT32_MAX) { 218 seterr(&snmp_client, 219 "bad index: integer too large"); 220 goto err; 221 } 222 *(int32_t *)(void *)((u_char *)e + 223 work->descr->entries[i].offset) = var->subs[p++]; 224 break; 225 226 case SNMP_SYNTAX_OCTETSTRING: 227 if (var->len < p + 1) { 228 seterr(&snmp_client, 229 "bad index: need string length"); 230 goto err; 231 } 232 len = var->subs[p++]; 233 if (var->len < p + len) { 234 seterr(&snmp_client, 235 "bad index: string too short"); 236 goto err; 237 } 238 if ((ptr = malloc(len + 1)) == NULL) { 239 seterr(&snmp_client, 240 "no memory for index string"); 241 goto err; 242 } 243 for (j = 0; j < len; j++) { 244 if (var->subs[p] > UCHAR_MAX) { 245 seterr(&snmp_client, 246 "bad index: char too large"); 247 free(ptr); 248 goto err; 249 } 250 ptr[j] = var->subs[p++]; 251 } 252 ptr[j] = '\0'; 253 *(u_char **)(void *)((u_char *)e + 254 work->descr->entries[i].offset) = ptr; 255 *(size_t *)(void *)((u_char *)e + 256 work->descr->entries[i].offset + sizeof(u_char *)) 257 = len; 258 break; 259 260 case SNMP_SYNTAX_OID: 261 if (var->len < p + 1) { 262 seterr(&snmp_client, 263 "bad index: need oid length"); 264 goto err; 265 } 266 oid.len = var->subs[p++]; 267 if (var->len < p + oid.len) { 268 seterr(&snmp_client, 269 "bad index: oid too short"); 270 goto err; 271 } 272 for (j = 0; j < oid.len; j++) 273 oid.subs[j] = var->subs[p++]; 274 *(struct asn_oid *)(void *)((u_char *)e + 275 work->descr->entries[i].offset) = oid; 276 break; 277 278 case SNMP_SYNTAX_IPADDRESS: 279 if (var->len < p + 4) { 280 seterr(&snmp_client, 281 "bad index: need ip-address"); 282 goto err; 283 } 284 for (j = 0; j < 4; j++) { 285 if (var->subs[p] > 0xff) { 286 seterr(&snmp_client, 287 "bad index: ipaddress too large"); 288 goto err; 289 } 290 ((u_char *)e + 291 work->descr->entries[i].offset)[j] = 292 var->subs[p++]; 293 } 294 break; 295 296 case SNMP_SYNTAX_GAUGE: 297 if (var->len < p + 1) { 298 seterr(&snmp_client, 299 "bad index: need unsigned"); 300 goto err; 301 } 302 if (var->subs[p] > UINT32_MAX) { 303 seterr(&snmp_client, 304 "bad index: unsigned too large"); 305 goto err; 306 } 307 *(uint32_t *)(void *)((u_char *)e + 308 work->descr->entries[i].offset) = var->subs[p++]; 309 break; 310 311 case SNMP_SYNTAX_COUNTER: 312 case SNMP_SYNTAX_TIMETICKS: 313 case SNMP_SYNTAX_COUNTER64: 314 case SNMP_SYNTAX_NULL: 315 case SNMP_SYNTAX_NOSUCHOBJECT: 316 case SNMP_SYNTAX_NOSUCHINSTANCE: 317 case SNMP_SYNTAX_ENDOFMIBVIEW: 318 abort(); 319 } 320 e->found |= (uint64_t)1 << i; 321 } 322 323 /* link into the correct place */ 324 e1 = TAILQ_FIRST(work->table); 325 w1 = TAILQ_FIRST(&work->worklist); 326 while (e1 != NULL) { 327 if (asn_compare_oid(&w1->index, &w->index) > 0) 328 break; 329 e1 = TAILQ_NEXT(e1, link); 330 w1 = TAILQ_NEXT(w1, link); 331 } 332 if (e1 == NULL) { 333 TAILQ_INSERT_TAIL(work->table, e, link); 334 TAILQ_INSERT_TAIL(&work->worklist, w, link); 335 } else { 336 TAILQ_INSERT_BEFORE(e1, e, link); 337 TAILQ_INSERT_BEFORE(w1, w, link); 338 } 339 340 return (e); 341 342 err: 343 /* 344 * Error happend. Free all octet string index parts and the entry 345 * itself. 346 */ 347 for (i = 0; i < work->descr->index_size; i++) { 348 if (work->descr->entries[i].syntax == SNMP_SYNTAX_OCTETSTRING && 349 (e->found & ((uint64_t)1 << i))) 350 free(*(void **)(void *)((u_char *)e + 351 work->descr->entries[i].offset)); 352 } 353 free(e); 354 free(w); 355 return (NULL); 356} 357 358/* 359 * Assign the value 360 */ 361static int 362table_value(const struct snmp_table *descr, struct entry *e, 363 const struct snmp_value *b) 364{ 365 u_int i; 366 u_char *ptr; 367 368 for (i = descr->index_size; 369 descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++) 370 if (descr->entries[i].subid == 371 b->var.subs[descr->table.len + 1]) 372 break; 373 if (descr->entries[i].syntax == SNMP_SYNTAX_NULL) 374 return (0); 375 376 /* check syntax */ 377 if (b->syntax != descr->entries[i].syntax) { 378 seterr(&snmp_client, "bad syntax (%u instead of %u)", b->syntax, 379 descr->entries[i].syntax); 380 return (-1); 381 } 382 383 switch (b->syntax) { 384 385 case SNMP_SYNTAX_INTEGER: 386 *(int32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 387 b->v.integer; 388 break; 389 390 case SNMP_SYNTAX_OCTETSTRING: 391 if ((ptr = malloc(b->v.octetstring.len + 1)) == NULL) { 392 seterr(&snmp_client, "no memory for string"); 393 return (-1); 394 } 395 memcpy(ptr, b->v.octetstring.octets, b->v.octetstring.len); 396 ptr[b->v.octetstring.len] = '\0'; 397 *(u_char **)(void *)((u_char *)e + descr->entries[i].offset) = 398 ptr; 399 *(size_t *)(void *)((u_char *)e + descr->entries[i].offset + 400 sizeof(u_char *)) = b->v.octetstring.len; 401 break; 402 403 case SNMP_SYNTAX_OID: 404 *(struct asn_oid *)(void *)((u_char *)e + descr->entries[i].offset) = 405 b->v.oid; 406 break; 407 408 case SNMP_SYNTAX_IPADDRESS: 409 memcpy((u_char *)e + descr->entries[i].offset, 410 b->v.ipaddress, 4); 411 break; 412 413 case SNMP_SYNTAX_COUNTER: 414 case SNMP_SYNTAX_GAUGE: 415 case SNMP_SYNTAX_TIMETICKS: 416 *(uint32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 417 b->v.uint32; 418 break; 419 420 case SNMP_SYNTAX_COUNTER64: 421 *(uint64_t *)(void *)((u_char *)e + descr->entries[i].offset) = 422 b->v.counter64; 423 break; 424 425 case SNMP_SYNTAX_NULL: 426 case SNMP_SYNTAX_NOSUCHOBJECT: 427 case SNMP_SYNTAX_NOSUCHINSTANCE: 428 case SNMP_SYNTAX_ENDOFMIBVIEW: 429 abort(); 430 } 431 e->found |= (uint64_t)1 << i; 432 433 return (0); 434} 435 436/* 437 * Initialize the first PDU to send 438 */ 439static void 440table_init_pdu(const struct snmp_table *descr, struct snmp_pdu *pdu) 441{ 442 if (snmp_client.version == SNMP_V1) 443 snmp_pdu_create(pdu, SNMP_PDU_GETNEXT); 444 else { 445 snmp_pdu_create(pdu, SNMP_PDU_GETBULK); 446 pdu->error_index = 10; 447 } 448 if (descr->last_change.len != 0) { 449 pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 450 pdu->bindings[pdu->nbindings].var = descr->last_change; 451 pdu->nbindings++; 452 if (pdu->version != SNMP_V1) 453 pdu->error_status++; 454 } 455 pdu->bindings[pdu->nbindings].var = descr->table; 456 pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 457 pdu->nbindings++; 458} 459 460/* 461 * Return code: 462 * 0 - End Of Table 463 * -1 - Error 464 * -2 - Last change changed - again 465 * +1 - ok, continue 466 */ 467static int 468table_check_response(struct tabwork *work, const struct snmp_pdu *resp) 469{ 470 const struct snmp_value *b; 471 struct entry *e; 472 473 if (resp->error_status != SNMP_ERR_NOERROR) { 474 if (snmp_client.version == SNMP_V1 && 475 resp->error_status == SNMP_ERR_NOSUCHNAME && 476 resp->error_index == 477 (work->descr->last_change.len == 0) ? 1 : 2) 478 /* EOT */ 479 return (0); 480 /* Error */ 481 seterr(&snmp_client, "error fetching table: status=%d index=%d", 482 resp->error_status, resp->error_index); 483 return (-1); 484 } 485 486 for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) { 487 if (work->descr->last_change.len != 0 && b == resp->bindings) { 488 if (!asn_is_suboid(&work->descr->last_change, &b->var) || 489 b->var.len != work->descr->last_change.len + 1 || 490 b->var.subs[work->descr->last_change.len] != 0) { 491 seterr(&snmp_client, 492 "last_change: bad response"); 493 return (-1); 494 } 495 if (b->syntax != SNMP_SYNTAX_TIMETICKS) { 496 seterr(&snmp_client, 497 "last_change: bad syntax %u", b->syntax); 498 return (-1); 499 } 500 if (work->first) { 501 work->last_change = b->v.uint32; 502 work->first = 0; 503 504 } else if (work->last_change != b->v.uint32) { 505 if (++work->iter >= work->descr->max_iter) { 506 seterr(&snmp_client, 507 "max iteration count exceeded"); 508 return (-1); 509 } 510 table_free(work, 1); 511 return (-2); 512 } 513 514 continue; 515 } 516 if (!asn_is_suboid(&work->descr->table, &b->var) || 517 b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 518 return (0); 519 520 if ((e = table_find(work, &b->var)) == NULL) 521 return (-1); 522 if (table_value(work->descr, e, b)) 523 return (-1); 524 } 525 return (+1); 526} 527 528/* 529 * Check table consistency 530 */ 531static int 532table_check_cons(struct tabwork *work) 533{ 534 struct entry *e; 535 536 TAILQ_FOREACH(e, work->table, link) 537 if ((e->found & work->descr->req_mask) != 538 work->descr->req_mask) { 539 if (work->descr->last_change.len == 0) { 540 if (++work->iter >= work->descr->max_iter) { 541 seterr(&snmp_client, 542 "max iteration count exceeded"); 543 return (-1); 544 } 545 return (-2); 546 } 547 seterr(&snmp_client, "inconsistency detected %llx %llx", 548 e->found, work->descr->req_mask); 549 return (-1); 550 } 551 return (0); 552} 553 554/* 555 * Fetch a table. Returns 0 if ok, -1 on errors.
|
553 */ 554int 555snmp_table_fetch(const struct snmp_table *descr, void *list) 556{ 557 struct snmp_pdu resp; 558 struct tabwork work; 559 int ret; 560 561 work.descr = descr; 562 work.table = (struct table *)list; 563 work.iter = 0; 564 TAILQ_INIT(work.table); 565 TAILQ_INIT(&work.worklist); 566 work.callback = NULL; 567 work.arg = NULL; 568 569 again: 570 /* 571 * We come to this label when the code detects that the table 572 * has changed while fetching it. 573 */ 574 work.first = 1; 575 work.last_change = 0; 576 table_init_pdu(descr, &work.pdu); 577 578 for (;;) { 579 if (snmp_dialog(&work.pdu, &resp)) { 580 table_free(&work, 1); 581 return (-1); 582 } 583 if ((ret = table_check_response(&work, &resp)) == 0) { 584 snmp_pdu_free(&resp); 585 break; 586 } 587 if (ret == -1) { 588 snmp_pdu_free(&resp); 589 table_free(&work, 1); 590 return (-1); 591 } 592 if (ret == -2) { 593 snmp_pdu_free(&resp); 594 goto again; 595 } 596 597 work.pdu.bindings[work.pdu.nbindings - 1].var = 598 resp.bindings[resp.nbindings - 1].var; 599 600 snmp_pdu_free(&resp); 601 } 602 603 if ((ret = table_check_cons(&work)) == -1) { 604 table_free(&work, 1); 605 return (-1); 606 } 607 if (ret == -2) { 608 table_free(&work, 1); 609 goto again; 610 } 611 /* 612 * Free index list 613 */ 614 table_free(&work, 0); 615 return (0); 616} 617 618/* 619 * Callback for table 620 */ 621static void 622table_cb(struct snmp_pdu *req __unused, struct snmp_pdu *resp, void *arg) 623{ 624 struct tabwork *work = arg; 625 int ret; 626 627 if (resp == NULL) { 628 /* timeout */ 629 seterr(&snmp_client, "no response to fetch table request"); 630 table_free(work, 1); 631 work->callback(work->table, work->arg, -1); 632 free(work); 633 return; 634 } 635 636 if ((ret = table_check_response(work, resp)) == 0) { 637 /* EOT */ 638 snmp_pdu_free(resp); 639 640 if ((ret = table_check_cons(work)) == -1) { 641 /* error happend */ 642 table_free(work, 1); 643 work->callback(work->table, work->arg, -1); 644 free(work); 645 return; 646 } 647 if (ret == -2) { 648 /* restart */ 649 again: 650 table_free(work, 1); 651 work->first = 1; 652 work->last_change = 0; 653 table_init_pdu(work->descr, &work->pdu); 654 if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 655 work->callback(work->table, work->arg, -1); 656 free(work); 657 return; 658 } 659 return; 660 } 661 /* 662 * Free index list 663 */ 664 table_free(work, 0); 665 work->callback(work->table, work->arg, 0); 666 free(work); 667 return; 668 } 669 670 if (ret == -1) { 671 /* error */ 672 snmp_pdu_free(resp); 673 table_free(work, 1); 674 work->callback(work->table, work->arg, -1); 675 free(work); 676 return; 677 } 678 679 if (ret == -2) { 680 /* again */ 681 snmp_pdu_free(resp); 682 goto again; 683 } 684 685 /* next part */ 686 687 work->pdu.bindings[work->pdu.nbindings - 1].var = 688 resp->bindings[resp->nbindings - 1].var; 689 690 snmp_pdu_free(resp); 691 692 if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 693 table_free(work, 1); 694 work->callback(work->table, work->arg, -1); 695 free(work); 696 return; 697 } 698} 699 700int 701snmp_table_fetch_async(const struct snmp_table *descr, void *list, 702 snmp_table_cb_f func, void *arg) 703{ 704 struct tabwork *work; 705 706 if ((work = malloc(sizeof(*work))) == NULL) { 707 seterr(&snmp_client, "%s", strerror(errno)); 708 return (-1); 709 } 710 711 work->descr = descr; 712 work->table = (struct table *)list; 713 work->iter = 0; 714 TAILQ_INIT(work->table); 715 TAILQ_INIT(&work->worklist); 716 717 work->callback = func; 718 work->arg = arg; 719 720 /* 721 * Start by sending the first PDU 722 */ 723 work->first = 1; 724 work->last_change = 0; 725 table_init_pdu(descr, &work->pdu); 726 727 if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) 728 return (-1); 729 return (0); 730} 731 732/* 733 * Append an index to an oid 734 */ 735int 736snmp_oid_append(struct asn_oid *oid, const char *fmt, ...) 737{ 738 va_list va; 739 int size; 740 char *nextptr; 741 const u_char *str; 742 size_t len; 743 struct in_addr ina; 744 int ret; 745 746 va_start(va, fmt); 747 748 size = 0; 749 750 ret = 0; 751 while (*fmt != '\0') { 752 switch (*fmt++) { 753 case 'i': 754 /* just an integer more */ 755 if (oid->len + 1 > ASN_MAXOIDLEN) { 756 warnx("%s: OID too long for integer", __func__); 757 ret = -1; 758 break; 759 } 760 oid->subs[oid->len++] = va_arg(va, asn_subid_t); 761 break; 762 763 case 'a': 764 /* append an IP address */ 765 if (oid->len + 4 > ASN_MAXOIDLEN) { 766 warnx("%s: OID too long for ip-addr", __func__); 767 ret = -1; 768 break; 769 } 770 ina = va_arg(va, struct in_addr); 771 ina.s_addr = ntohl(ina.s_addr); 772 oid->subs[oid->len++] = (ina.s_addr >> 24) & 0xff; 773 oid->subs[oid->len++] = (ina.s_addr >> 16) & 0xff; 774 oid->subs[oid->len++] = (ina.s_addr >> 8) & 0xff; 775 oid->subs[oid->len++] = (ina.s_addr >> 0) & 0xff; 776 break; 777 778 case 's': 779 /* append a null-terminated string, 780 * length is computed */ 781 str = (const u_char *)va_arg(va, const char *); 782 len = strlen((const char *)str); 783 if (oid->len + len + 1 > ASN_MAXOIDLEN) { 784 warnx("%s: OID too long for string", __func__); 785 ret = -1; 786 break; 787 } 788 oid->subs[oid->len++] = len; 789 while (len--) 790 oid->subs[oid->len++] = *str++; 791 break; 792 793 case '(': 794 /* the integer value between ( and ) is stored 795 * in size */ 796 size = strtol(fmt, &nextptr, 10); 797 if (*nextptr != ')') 798 abort(); 799 fmt = ++nextptr; 800 break; 801 802 case 'b': 803 /* append `size` characters */ 804 str = (const u_char *)va_arg(va, const char *); 805 if (oid->len + size > ASN_MAXOIDLEN) { 806 warnx("%s: OID too long for string", __func__); 807 ret = -1; 808 break; 809 } 810 while (size--) 811 oid->subs[oid->len++] = *str++; 812 break; 813 814 case 'c': 815 /* get size and the octets from the arguments */ 816 size = va_arg(va, size_t); 817 str = va_arg(va, const u_char *); 818 if (oid->len + size + 1 > ASN_MAXOIDLEN) { 819 warnx("%s: OID too long for string", __func__); 820 ret = -1; 821 break; 822 } 823 oid->subs[oid->len++] = size; 824 while (size--) 825 oid->subs[oid->len++] = *str++; 826 break; 827 828 default: 829 abort(); 830 } 831 } 832 va_end(va); 833 return (ret); 834} 835 836/* 837 * Initialize a client structure 838 */ 839void 840snmp_client_init(struct snmp_client *c) 841{ 842 memset(c, 0, sizeof(*c)); 843 844 c->version = SNMP_V2c; 845 c->trans = SNMP_TRANS_UDP; 846 c->chost = NULL; 847 c->cport = NULL; 848 849 strcpy(c->read_community, "public"); 850 strcpy(c->write_community, "private"); 851 852 c->timeout.tv_sec = 3; 853 c->timeout.tv_usec = 0; 854 c->retries = 3; 855 c->dump_pdus = 0; 856 c->txbuflen = c->rxbuflen = 10000; 857 858 c->fd = -1; 859 860 c->max_reqid = INT32_MAX; 861 c->min_reqid = 0; 862 c->next_reqid = 0; 863} 864 865 866/* 867 * Open UDP client socket 868 */ 869static int 870open_client_udp(const char *host, const char *port) 871{ 872 int error; 873 char *ptr; 874 struct addrinfo hints, *res0, *res; 875 876 /* copy host- and portname */ 877 if (snmp_client.chost == NULL) { 878 if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_HOST))) 879 == NULL) { 880 seterr(&snmp_client, "%s", strerror(errno)); 881 return (-1); 882 } 883 strcpy(snmp_client.chost, DEFAULT_HOST); 884 } 885 if (host != NULL) { 886 if ((ptr = malloc(1 + strlen(host))) == NULL) { 887 seterr(&snmp_client, "%s", strerror(errno)); 888 return (-1); 889 } 890 free(snmp_client.chost); 891 snmp_client.chost = ptr; 892 strcpy(snmp_client.chost, host); 893 } 894 if (snmp_client.cport == NULL) { 895 if ((snmp_client.cport = malloc(1 + sizeof(DEFAULT_PORT))) 896 == NULL) { 897 seterr(&snmp_client, "%s", strerror(errno)); 898 return (-1); 899 } 900 strcpy(snmp_client.cport, DEFAULT_PORT); 901 } 902 if (port != NULL) { 903 if ((ptr = malloc(1 + strlen(port))) == NULL) { 904 seterr(&snmp_client, "%s", strerror(errno)); 905 return (-1); 906 } 907 free(snmp_client.cport); 908 snmp_client.cport = ptr; 909 strcpy(snmp_client.cport, port); 910 } 911 912 /* open connection */ 913 memset(&hints, 0, sizeof(hints)); 914 hints.ai_flags = AI_CANONNAME; 915 hints.ai_family = AF_INET; 916 hints.ai_socktype = SOCK_DGRAM; 917 hints.ai_protocol = 0; 918 error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0); 919 if (error != 0) { 920 seterr(&snmp_client, "%s: %s", snmp_client.chost, 921 gai_strerror(error)); 922 return (-1); 923 } 924 res = res0; 925 for (;;) { 926 if ((snmp_client.fd = socket(res->ai_family, res->ai_socktype, 927 res->ai_protocol)) == -1) { 928 if ((res = res->ai_next) == NULL) { 929 seterr(&snmp_client, "%s", strerror(errno)); 930 freeaddrinfo(res0); 931 return (-1); 932 } 933 } else if (connect(snmp_client.fd, res->ai_addr, 934 res->ai_addrlen) == -1) { 935 if ((res = res->ai_next) == NULL) { 936 seterr(&snmp_client, "%s", strerror(errno)); 937 freeaddrinfo(res0); 938 return (-1); 939 } 940 } else 941 break; 942 } 943 freeaddrinfo(res0); 944 return (0); 945} 946 947static void 948remove_local(void) 949{ 950 (void)remove(snmp_client.local_path); 951} 952 953/* 954 * Open local socket 955 */ 956static int 957open_client_local(const char *path) 958{ 959 struct sockaddr_un sa; 960 char *ptr; 961 int stype; 962 963 if (snmp_client.chost == NULL) { 964 if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_LOCAL))) 965 == NULL) { 966 seterr(&snmp_client, "%s", strerror(errno)); 967 return (-1); 968 } 969 strcpy(snmp_client.chost, DEFAULT_LOCAL); 970 } 971 if (path != NULL) { 972 if ((ptr = malloc(1 + strlen(path))) == NULL) { 973 seterr(&snmp_client, "%s", strerror(errno)); 974 return (-1); 975 } 976 free(snmp_client.chost); 977 snmp_client.chost = ptr; 978 strcpy(snmp_client.chost, path); 979 } 980 981 if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM) 982 stype = SOCK_DGRAM; 983 else 984 stype = SOCK_STREAM; 985 986 if ((snmp_client.fd = socket(PF_LOCAL, stype, 0)) == -1) { 987 seterr(&snmp_client, "%s", strerror(errno)); 988 return (-1); 989 } 990 991 snprintf(snmp_client.local_path, sizeof(snmp_client.local_path), 992 "%s", SNMP_LOCAL_PATH); 993 994 if (mktemp(snmp_client.local_path) == NULL) { 995 seterr(&snmp_client, "%s", strerror(errno)); 996 (void)close(snmp_client.fd); 997 snmp_client.fd = -1; 998 return (-1); 999 } 1000 1001 sa.sun_family = AF_LOCAL; 1002 sa.sun_len = sizeof(sa); 1003 strcpy(sa.sun_path, snmp_client.local_path); 1004 1005 if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 1006 seterr(&snmp_client, "%s", strerror(errno)); 1007 (void)close(snmp_client.fd); 1008 snmp_client.fd = -1; 1009 (void)remove(snmp_client.local_path); 1010 return (-1); 1011 } 1012 atexit(remove_local); 1013 1014 sa.sun_family = AF_LOCAL; 1015 sa.sun_len = offsetof(struct sockaddr_un, sun_path) + 1016 strlen(snmp_client.chost); 1017 strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1); 1018 sa.sun_path[sizeof(sa.sun_path) - 1] = '\0'; 1019 1020 if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) { 1021 seterr(&snmp_client, "%s", strerror(errno)); 1022 (void)close(snmp_client.fd); 1023 snmp_client.fd = -1; 1024 (void)remove(snmp_client.local_path); 1025 return (-1); 1026 } 1027 return (0); 1028} 1029 1030/* 1031 * SNMP_OPEN 1032 */ 1033int 1034snmp_open(const char *host, const char *port, const char *readcomm, 1035 const char *writecomm) 1036{ 1037 struct timeval tout; 1038 1039 /* still open ? */ 1040 if (snmp_client.fd != -1) { 1041 errno = EBUSY; 1042 seterr(&snmp_client, "%s", strerror(errno)); 1043 return (-1); 1044 } 1045 1046 /* copy community strings */ 1047 if (readcomm != NULL) 1048 strlcpy(snmp_client.read_community, readcomm, 1049 sizeof(snmp_client.read_community)); 1050 if (writecomm != NULL) 1051 strlcpy(snmp_client.write_community, writecomm, 1052 sizeof(snmp_client.write_community)); 1053 1054 switch (snmp_client.trans) { 1055 1056 case SNMP_TRANS_UDP: 1057 if (open_client_udp(host, port)) 1058 return (-1); 1059 break; 1060 1061 case SNMP_TRANS_LOC_DGRAM: 1062 case SNMP_TRANS_LOC_STREAM: 1063 if (open_client_local(host)) 1064 return (-1); 1065 break; 1066 1067 default: 1068 seterr(&snmp_client, "bad transport mapping"); 1069 return (-1); 1070 } 1071 tout.tv_sec = 0; 1072 tout.tv_usec = 0; 1073 if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_SNDTIMEO, 1074 &tout, sizeof(struct timeval)) == -1) { 1075 seterr(&snmp_client, "%s", strerror(errno)); 1076 (void)close(snmp_client.fd); 1077 snmp_client.fd = -1; 1078 if (snmp_client.local_path[0] != '\0') 1079 (void)remove(snmp_client.local_path); 1080 return (-1); 1081 } 1082 1083 /* initialize list */ 1084 LIST_INIT(&sent_pdus); 1085 1086 return (0); 1087} 1088 1089 1090/* 1091 * SNMP_CLOSE 1092 * 1093 * closes connection to snmp server 1094 * - function cannot fail 1095 * - clears connection 1096 * - clears list of sent pdus 1097 * 1098 * input: 1099 * void 1100 * return: 1101 * void 1102 */ 1103void 1104snmp_close(void) 1105{ 1106 struct sent_pdu *p1; 1107 1108 if (snmp_client.fd != -1) { 1109 (void)close(snmp_client.fd); 1110 snmp_client.fd = -1; 1111 if (snmp_client.local_path[0] != '\0') 1112 (void)remove(snmp_client.local_path); 1113 } 1114 while(!LIST_EMPTY(&sent_pdus)){ 1115 p1 = LIST_FIRST(&sent_pdus); 1116 if (p1->timeout_id != NULL) 1117 snmp_client.timeout_stop(p1->timeout_id); 1118 LIST_REMOVE(p1, entries); 1119 free(p1); 1120 } 1121 free(snmp_client.chost); 1122 free(snmp_client.cport); 1123} 1124 1125/* 1126 * initialize a snmp_pdu structure 1127 */ 1128void 1129snmp_pdu_create(struct snmp_pdu *pdu, u_int op) 1130{ 1131 memset(pdu,0,sizeof(struct snmp_pdu)); 1132 if (op == SNMP_PDU_SET) 1133 strlcpy(pdu->community, snmp_client.write_community, 1134 sizeof(pdu->community)); 1135 else 1136 strlcpy(pdu->community, snmp_client.read_community, 1137 sizeof(pdu->community)); 1138 1139 pdu->type = op; 1140 pdu->version = snmp_client.version; 1141 pdu->error_status = 0; 1142 pdu->error_index = 0; 1143 pdu->nbindings = 0; 1144} 1145 1146/* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */ 1147/* added 10/04/02 by kek: check for MAX_BINDINGS */ 1148int 1149snmp_add_binding(struct snmp_v1_pdu *pdu, ...) 1150{ 1151 va_list ap; 1152 const struct asn_oid *oid; 1153 u_int ret; 1154 1155 va_start(ap, pdu); 1156 1157 ret = pdu->nbindings; 1158 while ((oid = va_arg(ap, const struct asn_oid *)) != NULL) { 1159 if (pdu->nbindings >= SNMP_MAX_BINDINGS){ 1160 va_end(ap); 1161 return (-1); 1162 } 1163 pdu->bindings[pdu->nbindings].var = *oid; 1164 pdu->bindings[pdu->nbindings].syntax = 1165 va_arg(ap, enum snmp_syntax); 1166 pdu->nbindings++; 1167 } 1168 va_end(ap); 1169 return (ret); 1170} 1171 1172 1173static int32_t 1174snmp_next_reqid(struct snmp_client * c) 1175{ 1176 int32_t i; 1177 1178 i = c->next_reqid; 1179 if (c->next_reqid >= c->max_reqid) 1180 c->next_reqid = c->min_reqid; 1181 else 1182 c->next_reqid++; 1183 return (i); 1184} 1185 1186/* 1187 * Send request and return request id. 1188 */ 1189static int32_t 1190snmp_send_packet(struct snmp_pdu * pdu) 1191{ 1192 u_char *buf; 1193 struct asn_buf b; 1194 ssize_t ret; 1195 1196 if ((buf = malloc(snmp_client.txbuflen)) == NULL) { 1197 seterr(&snmp_client, "%s", strerror(errno)); 1198 return (-1); 1199 } 1200 1201 pdu->request_id = snmp_next_reqid(&snmp_client); 1202 1203 b.asn_ptr = buf; 1204 b.asn_len = snmp_client.txbuflen; 1205 if (snmp_pdu_encode(pdu, &b)) { 1206 seterr(&snmp_client, "%s", strerror(errno)); 1207 free(buf); 1208 return (-1); 1209 } 1210 1211 if (snmp_client.dump_pdus) 1212 snmp_pdu_dump(pdu); 1213 1214 if ((ret = send(snmp_client.fd, buf, b.asn_ptr - buf, 0)) == -1) { 1215 seterr(&snmp_client, "%s", strerror(errno)); 1216 free(buf); 1217 return (-1); 1218 } 1219 free(buf); 1220 1221 return pdu->request_id; 1222} 1223 1224/* 1225 * to be called when a snmp request timed out 1226 */ 1227static void 1228snmp_timeout(void * listentry_ptr) 1229{ 1230 struct sent_pdu *listentry = listentry_ptr; 1231 1232#if 0 1233 warnx("snmp request %i timed out, attempt (%i/%i)", 1234 listentry->reqid, listentry->retrycount, snmp_client.retries); 1235#endif 1236 1237 listentry->retrycount++; 1238 if (listentry->retrycount > snmp_client.retries) { 1239 /* there is no answer at all */ 1240 LIST_REMOVE(listentry, entries); 1241 listentry->callback(listentry->pdu, NULL, listentry->arg); 1242 free(listentry); 1243 } else { 1244 /* try again */ 1245 /* new request with new request ID */ 1246 listentry->reqid = snmp_send_packet(listentry->pdu); 1247 listentry->timeout_id = 1248 snmp_client.timeout_start(&snmp_client.timeout, 1249 snmp_timeout, listentry); 1250 } 1251} 1252 1253int32_t 1254snmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *arg) 1255{ 1256 struct sent_pdu *listentry; 1257 int32_t id; 1258 1259 if ((listentry = malloc(sizeof(struct sent_pdu))) == NULL) { 1260 seterr(&snmp_client, "%s", strerror(errno)); 1261 return (-1); 1262 } 1263 1264 /* here we really send */ 1265 if ((id = snmp_send_packet(pdu)) == -1) { 1266 free(listentry); 1267 return (-1); 1268 } 1269 1270 /* add entry to list of sent PDUs */ 1271 listentry->pdu = pdu; 1272 if (gettimeofday(&listentry->time, NULL) == -1) 1273 warn("gettimeofday() failed"); 1274 1275 listentry->reqid = pdu->request_id; 1276 listentry->callback = func; 1277 listentry->arg = arg; 1278 listentry->retrycount=1; 1279 listentry->timeout_id = 1280 snmp_client.timeout_start(&snmp_client.timeout, snmp_timeout, 1281 listentry); 1282 1283 LIST_INSERT_HEAD(&sent_pdus, listentry, entries); 1284 1285 return (id); 1286} 1287 1288/* 1289 * Receive an SNMP packet. 1290 * 1291 * tv controls how we wait for a packet: if tv is a NULL pointer, 1292 * the receive blocks forever, if tv points to a structure with all 1293 * members 0 the socket is polled, in all other cases tv specifies the 1294 * maximum time to wait for a packet. 1295 * 1296 * Return: 1297 * -1 on errors 1298 * 0 on timeout 1299 * +1 if packet received 1300 */ 1301static int 1302snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv) 1303{ 1304 int dopoll, setpoll; 1305 int flags; 1306 int saved_errno; 1307 u_char *buf; 1308 int ret; 1309 struct asn_buf abuf; 1310 int32_t ip; 1311#ifdef bsdi 1312 int optlen; 1313#else 1314 socklen_t optlen; 1315#endif 1316 1317 if ((buf = malloc(snmp_client.rxbuflen)) == NULL) { 1318 seterr(&snmp_client, "%s", strerror(errno)); 1319 return (-1); 1320 } 1321 dopoll = setpoll = 0; 1322 flags = 0; 1323 if (tv != NULL) { 1324 /* poll or timeout */ 1325 if (tv->tv_sec != 0 || tv->tv_usec != 0) { 1326 /* wait with timeout */ 1327 if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1328 tv, sizeof(*tv)) == -1) { 1329 seterr(&snmp_client, "setsockopt: %s", 1330 strerror(errno)); 1331 free(buf); 1332 return (-1); 1333 } 1334 optlen = sizeof(*tv); 1335 if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1336 tv, &optlen) == -1) { 1337 seterr(&snmp_client, "getsockopt: %s", 1338 strerror(errno)); 1339 free(buf); 1340 return (-1); 1341 } 1342 /* at this point tv_sec and tv_usec may appear 1343 * as 0. This happens for timeouts lesser than 1344 * the clock granularity. The kernel rounds these to 1345 * 0 and this would result in a blocking receive. 1346 * Instead of an else we check tv_sec and tv_usec 1347 * again below and if this rounding happens, 1348 * switch to a polling receive. */ 1349 } 1350 if (tv->tv_sec == 0 && tv->tv_usec == 0) { 1351 /* poll */ 1352 dopoll = 1; 1353 if ((flags = fcntl(snmp_client.fd, F_GETFL, 0)) == -1) { 1354 seterr(&snmp_client, "fcntl: %s", 1355 strerror(errno)); 1356 free(buf); 1357 return (-1); 1358 } 1359 if (!(flags & O_NONBLOCK)) { 1360 setpoll = 1; 1361 flags |= O_NONBLOCK; 1362 if (fcntl(snmp_client.fd, F_SETFL, flags) == -1) { 1363 seterr(&snmp_client, "fcntl: %s", 1364 strerror(errno)); 1365 free(buf); 1366 return (-1); 1367 } 1368 } 1369 } 1370 } 1371 ret = recv(snmp_client.fd, buf, snmp_client.rxbuflen, 0); 1372 saved_errno = errno; 1373 if (tv != NULL) { 1374 if (dopoll) { 1375 if (setpoll) { 1376 flags &= ~O_NONBLOCK; 1377 (void)fcntl(snmp_client.fd, F_SETFL, flags); 1378 } 1379 } else { 1380 tv->tv_sec = 0; 1381 tv->tv_usec = 0; 1382 (void)setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1383 tv, sizeof(*tv)); 1384 } 1385 } 1386 if (ret == -1) { 1387 free(buf); 1388 if (errno == EAGAIN || errno == EWOULDBLOCK) 1389 return (0); 1390 seterr(&snmp_client, "recv: %s", strerror(saved_errno)); 1391 return (-1); 1392 } 1393 if (ret == 0) { 1394 /* this happens when we have a streaming socket and the 1395 * remote side has closed it */ 1396 free(buf); 1397 seterr(&snmp_client, "recv: socket closed by peer"); 1398 errno = EPIPE; 1399 return (-1); 1400 } 1401 1402 abuf.asn_ptr = buf; 1403 abuf.asn_len = ret; 1404 1405 if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) { 1406 seterr(&snmp_client, "snmp_decode_pdu: failed %d", ret); 1407 free(buf); 1408 return (-1); 1409 } 1410 free(buf); 1411 if (snmp_client.dump_pdus) 1412 snmp_pdu_dump(pdu); 1413 1414 return (+1); 1415} 1416 1417static int 1418snmp_deliver_packet(struct snmp_pdu * resp) 1419{ 1420 struct sent_pdu *listentry; 1421 1422 if (resp->type != SNMP_PDU_RESPONSE) { 1423 warn("ignoring snmp pdu %u", resp->type); 1424 return (-1); 1425 } 1426 1427 LIST_FOREACH(listentry, &sent_pdus, entries) 1428 if (listentry->reqid == resp->request_id) 1429 break; 1430 if (listentry == NULL) 1431 return (-1); 1432 1433 LIST_REMOVE(listentry, entries); 1434 listentry->callback(listentry->pdu, resp, listentry->arg); 1435 1436 snmp_client.timeout_stop(listentry->timeout_id); 1437 1438 free(listentry); 1439 return (0); 1440} 1441 1442int 1443snmp_receive(int blocking) 1444{ 1445 int ret; 1446 1447 struct timeval tv; 1448 struct snmp_pdu * resp; 1449 1450 memset(&tv, 0, sizeof(tv)); 1451 1452 resp = malloc(sizeof(struct snmp_pdu)); 1453 if (resp == NULL) { 1454 seterr(&snmp_client, "no memory for returning PDU"); 1455 return (-1) ; 1456 } 1457 1458 if ((ret = snmp_receive_packet(resp, blocking ? NULL : &tv)) <= 0) { 1459 free(resp); 1460 return (ret); 1461 } 1462 ret = snmp_deliver_packet(resp); 1463 snmp_pdu_free(resp); 1464 free(resp); 1465 return (ret); 1466} 1467 1468 1469/* 1470 * Check a GETNEXT response. Here we have three possible outcomes: -1 an 1471 * unexpected error happened. +1 response is ok and is within the table 0 1472 * response is ok, but is behind the table or error is NOSUCHNAME. The req 1473 * should point to a template PDU which contains the base OIDs and the 1474 * syntaxes. This is really only useful to sweep non-sparse tables. 1475 */ 1476static int 1477ok_getnext(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1478{ 1479 u_int i; 1480 1481 if (resp->version != req->version) { 1482 warnx("SNMP GETNEXT: response has wrong version"); 1483 return (-1); 1484 } 1485 1486 if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1487 return (0); 1488 1489 if (resp->error_status != SNMP_ERR_NOERROR) { 1490 warnx("SNMP GETNEXT: error %d", resp->error_status); 1491 return (-1); 1492 } 1493 if (resp->nbindings != req->nbindings) { 1494 warnx("SNMP GETNEXT: bad number of bindings in response"); 1495 return (-1); 1496 } 1497 for (i = 0; i < req->nbindings; i++) { 1498 if (!asn_is_suboid(&req->bindings[i].var, 1499 &resp->bindings[i].var)) { 1500 if (i != 0) 1501 warnx("SNMP GETNEXT: inconsistent table " 1502 "response"); 1503 return (0); 1504 } 1505 if (resp->version != SNMP_V1 && 1506 resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 1507 return (0); 1508 1509 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1510 warnx("SNMP GETNEXT: bad syntax in response"); 1511 return (0); 1512 } 1513 } 1514 return (1); 1515} 1516 1517/* 1518 * Check a GET response. Here we have three possible outcomes: -1 an 1519 * unexpected error happened. +1 response is ok. 0 NOSUCHNAME The req should 1520 * point to a template PDU which contains the OIDs and the syntaxes. This 1521 * is only useful for SNMPv1 or single object GETS. 1522 */ 1523static int 1524ok_get(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1525{ 1526 u_int i; 1527 1528 if (resp->version != req->version) { 1529 warnx("SNMP GET: response has wrong version"); 1530 return (-1); 1531 } 1532 1533 if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1534 return (0); 1535 1536 if (resp->error_status != SNMP_ERR_NOERROR) { 1537 warnx("SNMP GET: error %d", resp->error_status); 1538 return (-1); 1539 } 1540 1541 if (resp->nbindings != req->nbindings) { 1542 warnx("SNMP GET: bad number of bindings in response"); 1543 return (-1); 1544 } 1545 for (i = 0; i < req->nbindings; i++) { 1546 if (asn_compare_oid(&req->bindings[i].var, 1547 &resp->bindings[i].var) != 0) { 1548 warnx("SNMP GET: bad OID in response"); 1549 return (-1); 1550 } 1551 if (snmp_client.version != SNMP_V1 && 1552 (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT || 1553 resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE)) 1554 return (0); 1555 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1556 warnx("SNMP GET: bad syntax in response"); 1557 return (-1); 1558 } 1559 } 1560 return (1); 1561} 1562 1563/*
| 557 */ 558int 559snmp_table_fetch(const struct snmp_table *descr, void *list) 560{ 561 struct snmp_pdu resp; 562 struct tabwork work; 563 int ret; 564 565 work.descr = descr; 566 work.table = (struct table *)list; 567 work.iter = 0; 568 TAILQ_INIT(work.table); 569 TAILQ_INIT(&work.worklist); 570 work.callback = NULL; 571 work.arg = NULL; 572 573 again: 574 /* 575 * We come to this label when the code detects that the table 576 * has changed while fetching it. 577 */ 578 work.first = 1; 579 work.last_change = 0; 580 table_init_pdu(descr, &work.pdu); 581 582 for (;;) { 583 if (snmp_dialog(&work.pdu, &resp)) { 584 table_free(&work, 1); 585 return (-1); 586 } 587 if ((ret = table_check_response(&work, &resp)) == 0) { 588 snmp_pdu_free(&resp); 589 break; 590 } 591 if (ret == -1) { 592 snmp_pdu_free(&resp); 593 table_free(&work, 1); 594 return (-1); 595 } 596 if (ret == -2) { 597 snmp_pdu_free(&resp); 598 goto again; 599 } 600 601 work.pdu.bindings[work.pdu.nbindings - 1].var = 602 resp.bindings[resp.nbindings - 1].var; 603 604 snmp_pdu_free(&resp); 605 } 606 607 if ((ret = table_check_cons(&work)) == -1) { 608 table_free(&work, 1); 609 return (-1); 610 } 611 if (ret == -2) { 612 table_free(&work, 1); 613 goto again; 614 } 615 /* 616 * Free index list 617 */ 618 table_free(&work, 0); 619 return (0); 620} 621 622/* 623 * Callback for table 624 */ 625static void 626table_cb(struct snmp_pdu *req __unused, struct snmp_pdu *resp, void *arg) 627{ 628 struct tabwork *work = arg; 629 int ret; 630 631 if (resp == NULL) { 632 /* timeout */ 633 seterr(&snmp_client, "no response to fetch table request"); 634 table_free(work, 1); 635 work->callback(work->table, work->arg, -1); 636 free(work); 637 return; 638 } 639 640 if ((ret = table_check_response(work, resp)) == 0) { 641 /* EOT */ 642 snmp_pdu_free(resp); 643 644 if ((ret = table_check_cons(work)) == -1) { 645 /* error happend */ 646 table_free(work, 1); 647 work->callback(work->table, work->arg, -1); 648 free(work); 649 return; 650 } 651 if (ret == -2) { 652 /* restart */ 653 again: 654 table_free(work, 1); 655 work->first = 1; 656 work->last_change = 0; 657 table_init_pdu(work->descr, &work->pdu); 658 if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 659 work->callback(work->table, work->arg, -1); 660 free(work); 661 return; 662 } 663 return; 664 } 665 /* 666 * Free index list 667 */ 668 table_free(work, 0); 669 work->callback(work->table, work->arg, 0); 670 free(work); 671 return; 672 } 673 674 if (ret == -1) { 675 /* error */ 676 snmp_pdu_free(resp); 677 table_free(work, 1); 678 work->callback(work->table, work->arg, -1); 679 free(work); 680 return; 681 } 682 683 if (ret == -2) { 684 /* again */ 685 snmp_pdu_free(resp); 686 goto again; 687 } 688 689 /* next part */ 690 691 work->pdu.bindings[work->pdu.nbindings - 1].var = 692 resp->bindings[resp->nbindings - 1].var; 693 694 snmp_pdu_free(resp); 695 696 if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 697 table_free(work, 1); 698 work->callback(work->table, work->arg, -1); 699 free(work); 700 return; 701 } 702} 703 704int 705snmp_table_fetch_async(const struct snmp_table *descr, void *list, 706 snmp_table_cb_f func, void *arg) 707{ 708 struct tabwork *work; 709 710 if ((work = malloc(sizeof(*work))) == NULL) { 711 seterr(&snmp_client, "%s", strerror(errno)); 712 return (-1); 713 } 714 715 work->descr = descr; 716 work->table = (struct table *)list; 717 work->iter = 0; 718 TAILQ_INIT(work->table); 719 TAILQ_INIT(&work->worklist); 720 721 work->callback = func; 722 work->arg = arg; 723 724 /* 725 * Start by sending the first PDU 726 */ 727 work->first = 1; 728 work->last_change = 0; 729 table_init_pdu(descr, &work->pdu); 730 731 if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) 732 return (-1); 733 return (0); 734} 735 736/* 737 * Append an index to an oid 738 */ 739int 740snmp_oid_append(struct asn_oid *oid, const char *fmt, ...) 741{ 742 va_list va; 743 int size; 744 char *nextptr; 745 const u_char *str; 746 size_t len; 747 struct in_addr ina; 748 int ret; 749 750 va_start(va, fmt); 751 752 size = 0; 753 754 ret = 0; 755 while (*fmt != '\0') { 756 switch (*fmt++) { 757 case 'i': 758 /* just an integer more */ 759 if (oid->len + 1 > ASN_MAXOIDLEN) { 760 warnx("%s: OID too long for integer", __func__); 761 ret = -1; 762 break; 763 } 764 oid->subs[oid->len++] = va_arg(va, asn_subid_t); 765 break; 766 767 case 'a': 768 /* append an IP address */ 769 if (oid->len + 4 > ASN_MAXOIDLEN) { 770 warnx("%s: OID too long for ip-addr", __func__); 771 ret = -1; 772 break; 773 } 774 ina = va_arg(va, struct in_addr); 775 ina.s_addr = ntohl(ina.s_addr); 776 oid->subs[oid->len++] = (ina.s_addr >> 24) & 0xff; 777 oid->subs[oid->len++] = (ina.s_addr >> 16) & 0xff; 778 oid->subs[oid->len++] = (ina.s_addr >> 8) & 0xff; 779 oid->subs[oid->len++] = (ina.s_addr >> 0) & 0xff; 780 break; 781 782 case 's': 783 /* append a null-terminated string, 784 * length is computed */ 785 str = (const u_char *)va_arg(va, const char *); 786 len = strlen((const char *)str); 787 if (oid->len + len + 1 > ASN_MAXOIDLEN) { 788 warnx("%s: OID too long for string", __func__); 789 ret = -1; 790 break; 791 } 792 oid->subs[oid->len++] = len; 793 while (len--) 794 oid->subs[oid->len++] = *str++; 795 break; 796 797 case '(': 798 /* the integer value between ( and ) is stored 799 * in size */ 800 size = strtol(fmt, &nextptr, 10); 801 if (*nextptr != ')') 802 abort(); 803 fmt = ++nextptr; 804 break; 805 806 case 'b': 807 /* append `size` characters */ 808 str = (const u_char *)va_arg(va, const char *); 809 if (oid->len + size > ASN_MAXOIDLEN) { 810 warnx("%s: OID too long for string", __func__); 811 ret = -1; 812 break; 813 } 814 while (size--) 815 oid->subs[oid->len++] = *str++; 816 break; 817 818 case 'c': 819 /* get size and the octets from the arguments */ 820 size = va_arg(va, size_t); 821 str = va_arg(va, const u_char *); 822 if (oid->len + size + 1 > ASN_MAXOIDLEN) { 823 warnx("%s: OID too long for string", __func__); 824 ret = -1; 825 break; 826 } 827 oid->subs[oid->len++] = size; 828 while (size--) 829 oid->subs[oid->len++] = *str++; 830 break; 831 832 default: 833 abort(); 834 } 835 } 836 va_end(va); 837 return (ret); 838} 839 840/* 841 * Initialize a client structure 842 */ 843void 844snmp_client_init(struct snmp_client *c) 845{ 846 memset(c, 0, sizeof(*c)); 847 848 c->version = SNMP_V2c; 849 c->trans = SNMP_TRANS_UDP; 850 c->chost = NULL; 851 c->cport = NULL; 852 853 strcpy(c->read_community, "public"); 854 strcpy(c->write_community, "private"); 855 856 c->timeout.tv_sec = 3; 857 c->timeout.tv_usec = 0; 858 c->retries = 3; 859 c->dump_pdus = 0; 860 c->txbuflen = c->rxbuflen = 10000; 861 862 c->fd = -1; 863 864 c->max_reqid = INT32_MAX; 865 c->min_reqid = 0; 866 c->next_reqid = 0; 867} 868 869 870/* 871 * Open UDP client socket 872 */ 873static int 874open_client_udp(const char *host, const char *port) 875{ 876 int error; 877 char *ptr; 878 struct addrinfo hints, *res0, *res; 879 880 /* copy host- and portname */ 881 if (snmp_client.chost == NULL) { 882 if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_HOST))) 883 == NULL) { 884 seterr(&snmp_client, "%s", strerror(errno)); 885 return (-1); 886 } 887 strcpy(snmp_client.chost, DEFAULT_HOST); 888 } 889 if (host != NULL) { 890 if ((ptr = malloc(1 + strlen(host))) == NULL) { 891 seterr(&snmp_client, "%s", strerror(errno)); 892 return (-1); 893 } 894 free(snmp_client.chost); 895 snmp_client.chost = ptr; 896 strcpy(snmp_client.chost, host); 897 } 898 if (snmp_client.cport == NULL) { 899 if ((snmp_client.cport = malloc(1 + sizeof(DEFAULT_PORT))) 900 == NULL) { 901 seterr(&snmp_client, "%s", strerror(errno)); 902 return (-1); 903 } 904 strcpy(snmp_client.cport, DEFAULT_PORT); 905 } 906 if (port != NULL) { 907 if ((ptr = malloc(1 + strlen(port))) == NULL) { 908 seterr(&snmp_client, "%s", strerror(errno)); 909 return (-1); 910 } 911 free(snmp_client.cport); 912 snmp_client.cport = ptr; 913 strcpy(snmp_client.cport, port); 914 } 915 916 /* open connection */ 917 memset(&hints, 0, sizeof(hints)); 918 hints.ai_flags = AI_CANONNAME; 919 hints.ai_family = AF_INET; 920 hints.ai_socktype = SOCK_DGRAM; 921 hints.ai_protocol = 0; 922 error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0); 923 if (error != 0) { 924 seterr(&snmp_client, "%s: %s", snmp_client.chost, 925 gai_strerror(error)); 926 return (-1); 927 } 928 res = res0; 929 for (;;) { 930 if ((snmp_client.fd = socket(res->ai_family, res->ai_socktype, 931 res->ai_protocol)) == -1) { 932 if ((res = res->ai_next) == NULL) { 933 seterr(&snmp_client, "%s", strerror(errno)); 934 freeaddrinfo(res0); 935 return (-1); 936 } 937 } else if (connect(snmp_client.fd, res->ai_addr, 938 res->ai_addrlen) == -1) { 939 if ((res = res->ai_next) == NULL) { 940 seterr(&snmp_client, "%s", strerror(errno)); 941 freeaddrinfo(res0); 942 return (-1); 943 } 944 } else 945 break; 946 } 947 freeaddrinfo(res0); 948 return (0); 949} 950 951static void 952remove_local(void) 953{ 954 (void)remove(snmp_client.local_path); 955} 956 957/* 958 * Open local socket 959 */ 960static int 961open_client_local(const char *path) 962{ 963 struct sockaddr_un sa; 964 char *ptr; 965 int stype; 966 967 if (snmp_client.chost == NULL) { 968 if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_LOCAL))) 969 == NULL) { 970 seterr(&snmp_client, "%s", strerror(errno)); 971 return (-1); 972 } 973 strcpy(snmp_client.chost, DEFAULT_LOCAL); 974 } 975 if (path != NULL) { 976 if ((ptr = malloc(1 + strlen(path))) == NULL) { 977 seterr(&snmp_client, "%s", strerror(errno)); 978 return (-1); 979 } 980 free(snmp_client.chost); 981 snmp_client.chost = ptr; 982 strcpy(snmp_client.chost, path); 983 } 984 985 if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM) 986 stype = SOCK_DGRAM; 987 else 988 stype = SOCK_STREAM; 989 990 if ((snmp_client.fd = socket(PF_LOCAL, stype, 0)) == -1) { 991 seterr(&snmp_client, "%s", strerror(errno)); 992 return (-1); 993 } 994 995 snprintf(snmp_client.local_path, sizeof(snmp_client.local_path), 996 "%s", SNMP_LOCAL_PATH); 997 998 if (mktemp(snmp_client.local_path) == NULL) { 999 seterr(&snmp_client, "%s", strerror(errno)); 1000 (void)close(snmp_client.fd); 1001 snmp_client.fd = -1; 1002 return (-1); 1003 } 1004 1005 sa.sun_family = AF_LOCAL; 1006 sa.sun_len = sizeof(sa); 1007 strcpy(sa.sun_path, snmp_client.local_path); 1008 1009 if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 1010 seterr(&snmp_client, "%s", strerror(errno)); 1011 (void)close(snmp_client.fd); 1012 snmp_client.fd = -1; 1013 (void)remove(snmp_client.local_path); 1014 return (-1); 1015 } 1016 atexit(remove_local); 1017 1018 sa.sun_family = AF_LOCAL; 1019 sa.sun_len = offsetof(struct sockaddr_un, sun_path) + 1020 strlen(snmp_client.chost); 1021 strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1); 1022 sa.sun_path[sizeof(sa.sun_path) - 1] = '\0'; 1023 1024 if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) { 1025 seterr(&snmp_client, "%s", strerror(errno)); 1026 (void)close(snmp_client.fd); 1027 snmp_client.fd = -1; 1028 (void)remove(snmp_client.local_path); 1029 return (-1); 1030 } 1031 return (0); 1032} 1033 1034/* 1035 * SNMP_OPEN 1036 */ 1037int 1038snmp_open(const char *host, const char *port, const char *readcomm, 1039 const char *writecomm) 1040{ 1041 struct timeval tout; 1042 1043 /* still open ? */ 1044 if (snmp_client.fd != -1) { 1045 errno = EBUSY; 1046 seterr(&snmp_client, "%s", strerror(errno)); 1047 return (-1); 1048 } 1049 1050 /* copy community strings */ 1051 if (readcomm != NULL) 1052 strlcpy(snmp_client.read_community, readcomm, 1053 sizeof(snmp_client.read_community)); 1054 if (writecomm != NULL) 1055 strlcpy(snmp_client.write_community, writecomm, 1056 sizeof(snmp_client.write_community)); 1057 1058 switch (snmp_client.trans) { 1059 1060 case SNMP_TRANS_UDP: 1061 if (open_client_udp(host, port)) 1062 return (-1); 1063 break; 1064 1065 case SNMP_TRANS_LOC_DGRAM: 1066 case SNMP_TRANS_LOC_STREAM: 1067 if (open_client_local(host)) 1068 return (-1); 1069 break; 1070 1071 default: 1072 seterr(&snmp_client, "bad transport mapping"); 1073 return (-1); 1074 } 1075 tout.tv_sec = 0; 1076 tout.tv_usec = 0; 1077 if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_SNDTIMEO, 1078 &tout, sizeof(struct timeval)) == -1) { 1079 seterr(&snmp_client, "%s", strerror(errno)); 1080 (void)close(snmp_client.fd); 1081 snmp_client.fd = -1; 1082 if (snmp_client.local_path[0] != '\0') 1083 (void)remove(snmp_client.local_path); 1084 return (-1); 1085 } 1086 1087 /* initialize list */ 1088 LIST_INIT(&sent_pdus); 1089 1090 return (0); 1091} 1092 1093 1094/* 1095 * SNMP_CLOSE 1096 * 1097 * closes connection to snmp server 1098 * - function cannot fail 1099 * - clears connection 1100 * - clears list of sent pdus 1101 * 1102 * input: 1103 * void 1104 * return: 1105 * void 1106 */ 1107void 1108snmp_close(void) 1109{ 1110 struct sent_pdu *p1; 1111 1112 if (snmp_client.fd != -1) { 1113 (void)close(snmp_client.fd); 1114 snmp_client.fd = -1; 1115 if (snmp_client.local_path[0] != '\0') 1116 (void)remove(snmp_client.local_path); 1117 } 1118 while(!LIST_EMPTY(&sent_pdus)){ 1119 p1 = LIST_FIRST(&sent_pdus); 1120 if (p1->timeout_id != NULL) 1121 snmp_client.timeout_stop(p1->timeout_id); 1122 LIST_REMOVE(p1, entries); 1123 free(p1); 1124 } 1125 free(snmp_client.chost); 1126 free(snmp_client.cport); 1127} 1128 1129/* 1130 * initialize a snmp_pdu structure 1131 */ 1132void 1133snmp_pdu_create(struct snmp_pdu *pdu, u_int op) 1134{ 1135 memset(pdu,0,sizeof(struct snmp_pdu)); 1136 if (op == SNMP_PDU_SET) 1137 strlcpy(pdu->community, snmp_client.write_community, 1138 sizeof(pdu->community)); 1139 else 1140 strlcpy(pdu->community, snmp_client.read_community, 1141 sizeof(pdu->community)); 1142 1143 pdu->type = op; 1144 pdu->version = snmp_client.version; 1145 pdu->error_status = 0; 1146 pdu->error_index = 0; 1147 pdu->nbindings = 0; 1148} 1149 1150/* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */ 1151/* added 10/04/02 by kek: check for MAX_BINDINGS */ 1152int 1153snmp_add_binding(struct snmp_v1_pdu *pdu, ...) 1154{ 1155 va_list ap; 1156 const struct asn_oid *oid; 1157 u_int ret; 1158 1159 va_start(ap, pdu); 1160 1161 ret = pdu->nbindings; 1162 while ((oid = va_arg(ap, const struct asn_oid *)) != NULL) { 1163 if (pdu->nbindings >= SNMP_MAX_BINDINGS){ 1164 va_end(ap); 1165 return (-1); 1166 } 1167 pdu->bindings[pdu->nbindings].var = *oid; 1168 pdu->bindings[pdu->nbindings].syntax = 1169 va_arg(ap, enum snmp_syntax); 1170 pdu->nbindings++; 1171 } 1172 va_end(ap); 1173 return (ret); 1174} 1175 1176 1177static int32_t 1178snmp_next_reqid(struct snmp_client * c) 1179{ 1180 int32_t i; 1181 1182 i = c->next_reqid; 1183 if (c->next_reqid >= c->max_reqid) 1184 c->next_reqid = c->min_reqid; 1185 else 1186 c->next_reqid++; 1187 return (i); 1188} 1189 1190/* 1191 * Send request and return request id. 1192 */ 1193static int32_t 1194snmp_send_packet(struct snmp_pdu * pdu) 1195{ 1196 u_char *buf; 1197 struct asn_buf b; 1198 ssize_t ret; 1199 1200 if ((buf = malloc(snmp_client.txbuflen)) == NULL) { 1201 seterr(&snmp_client, "%s", strerror(errno)); 1202 return (-1); 1203 } 1204 1205 pdu->request_id = snmp_next_reqid(&snmp_client); 1206 1207 b.asn_ptr = buf; 1208 b.asn_len = snmp_client.txbuflen; 1209 if (snmp_pdu_encode(pdu, &b)) { 1210 seterr(&snmp_client, "%s", strerror(errno)); 1211 free(buf); 1212 return (-1); 1213 } 1214 1215 if (snmp_client.dump_pdus) 1216 snmp_pdu_dump(pdu); 1217 1218 if ((ret = send(snmp_client.fd, buf, b.asn_ptr - buf, 0)) == -1) { 1219 seterr(&snmp_client, "%s", strerror(errno)); 1220 free(buf); 1221 return (-1); 1222 } 1223 free(buf); 1224 1225 return pdu->request_id; 1226} 1227 1228/* 1229 * to be called when a snmp request timed out 1230 */ 1231static void 1232snmp_timeout(void * listentry_ptr) 1233{ 1234 struct sent_pdu *listentry = listentry_ptr; 1235 1236#if 0 1237 warnx("snmp request %i timed out, attempt (%i/%i)", 1238 listentry->reqid, listentry->retrycount, snmp_client.retries); 1239#endif 1240 1241 listentry->retrycount++; 1242 if (listentry->retrycount > snmp_client.retries) { 1243 /* there is no answer at all */ 1244 LIST_REMOVE(listentry, entries); 1245 listentry->callback(listentry->pdu, NULL, listentry->arg); 1246 free(listentry); 1247 } else { 1248 /* try again */ 1249 /* new request with new request ID */ 1250 listentry->reqid = snmp_send_packet(listentry->pdu); 1251 listentry->timeout_id = 1252 snmp_client.timeout_start(&snmp_client.timeout, 1253 snmp_timeout, listentry); 1254 } 1255} 1256 1257int32_t 1258snmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *arg) 1259{ 1260 struct sent_pdu *listentry; 1261 int32_t id; 1262 1263 if ((listentry = malloc(sizeof(struct sent_pdu))) == NULL) { 1264 seterr(&snmp_client, "%s", strerror(errno)); 1265 return (-1); 1266 } 1267 1268 /* here we really send */ 1269 if ((id = snmp_send_packet(pdu)) == -1) { 1270 free(listentry); 1271 return (-1); 1272 } 1273 1274 /* add entry to list of sent PDUs */ 1275 listentry->pdu = pdu; 1276 if (gettimeofday(&listentry->time, NULL) == -1) 1277 warn("gettimeofday() failed"); 1278 1279 listentry->reqid = pdu->request_id; 1280 listentry->callback = func; 1281 listentry->arg = arg; 1282 listentry->retrycount=1; 1283 listentry->timeout_id = 1284 snmp_client.timeout_start(&snmp_client.timeout, snmp_timeout, 1285 listentry); 1286 1287 LIST_INSERT_HEAD(&sent_pdus, listentry, entries); 1288 1289 return (id); 1290} 1291 1292/* 1293 * Receive an SNMP packet. 1294 * 1295 * tv controls how we wait for a packet: if tv is a NULL pointer, 1296 * the receive blocks forever, if tv points to a structure with all 1297 * members 0 the socket is polled, in all other cases tv specifies the 1298 * maximum time to wait for a packet. 1299 * 1300 * Return: 1301 * -1 on errors 1302 * 0 on timeout 1303 * +1 if packet received 1304 */ 1305static int 1306snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv) 1307{ 1308 int dopoll, setpoll; 1309 int flags; 1310 int saved_errno; 1311 u_char *buf; 1312 int ret; 1313 struct asn_buf abuf; 1314 int32_t ip; 1315#ifdef bsdi 1316 int optlen; 1317#else 1318 socklen_t optlen; 1319#endif 1320 1321 if ((buf = malloc(snmp_client.rxbuflen)) == NULL) { 1322 seterr(&snmp_client, "%s", strerror(errno)); 1323 return (-1); 1324 } 1325 dopoll = setpoll = 0; 1326 flags = 0; 1327 if (tv != NULL) { 1328 /* poll or timeout */ 1329 if (tv->tv_sec != 0 || tv->tv_usec != 0) { 1330 /* wait with timeout */ 1331 if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1332 tv, sizeof(*tv)) == -1) { 1333 seterr(&snmp_client, "setsockopt: %s", 1334 strerror(errno)); 1335 free(buf); 1336 return (-1); 1337 } 1338 optlen = sizeof(*tv); 1339 if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1340 tv, &optlen) == -1) { 1341 seterr(&snmp_client, "getsockopt: %s", 1342 strerror(errno)); 1343 free(buf); 1344 return (-1); 1345 } 1346 /* at this point tv_sec and tv_usec may appear 1347 * as 0. This happens for timeouts lesser than 1348 * the clock granularity. The kernel rounds these to 1349 * 0 and this would result in a blocking receive. 1350 * Instead of an else we check tv_sec and tv_usec 1351 * again below and if this rounding happens, 1352 * switch to a polling receive. */ 1353 } 1354 if (tv->tv_sec == 0 && tv->tv_usec == 0) { 1355 /* poll */ 1356 dopoll = 1; 1357 if ((flags = fcntl(snmp_client.fd, F_GETFL, 0)) == -1) { 1358 seterr(&snmp_client, "fcntl: %s", 1359 strerror(errno)); 1360 free(buf); 1361 return (-1); 1362 } 1363 if (!(flags & O_NONBLOCK)) { 1364 setpoll = 1; 1365 flags |= O_NONBLOCK; 1366 if (fcntl(snmp_client.fd, F_SETFL, flags) == -1) { 1367 seterr(&snmp_client, "fcntl: %s", 1368 strerror(errno)); 1369 free(buf); 1370 return (-1); 1371 } 1372 } 1373 } 1374 } 1375 ret = recv(snmp_client.fd, buf, snmp_client.rxbuflen, 0); 1376 saved_errno = errno; 1377 if (tv != NULL) { 1378 if (dopoll) { 1379 if (setpoll) { 1380 flags &= ~O_NONBLOCK; 1381 (void)fcntl(snmp_client.fd, F_SETFL, flags); 1382 } 1383 } else { 1384 tv->tv_sec = 0; 1385 tv->tv_usec = 0; 1386 (void)setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1387 tv, sizeof(*tv)); 1388 } 1389 } 1390 if (ret == -1) { 1391 free(buf); 1392 if (errno == EAGAIN || errno == EWOULDBLOCK) 1393 return (0); 1394 seterr(&snmp_client, "recv: %s", strerror(saved_errno)); 1395 return (-1); 1396 } 1397 if (ret == 0) { 1398 /* this happens when we have a streaming socket and the 1399 * remote side has closed it */ 1400 free(buf); 1401 seterr(&snmp_client, "recv: socket closed by peer"); 1402 errno = EPIPE; 1403 return (-1); 1404 } 1405 1406 abuf.asn_ptr = buf; 1407 abuf.asn_len = ret; 1408 1409 if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) { 1410 seterr(&snmp_client, "snmp_decode_pdu: failed %d", ret); 1411 free(buf); 1412 return (-1); 1413 } 1414 free(buf); 1415 if (snmp_client.dump_pdus) 1416 snmp_pdu_dump(pdu); 1417 1418 return (+1); 1419} 1420 1421static int 1422snmp_deliver_packet(struct snmp_pdu * resp) 1423{ 1424 struct sent_pdu *listentry; 1425 1426 if (resp->type != SNMP_PDU_RESPONSE) { 1427 warn("ignoring snmp pdu %u", resp->type); 1428 return (-1); 1429 } 1430 1431 LIST_FOREACH(listentry, &sent_pdus, entries) 1432 if (listentry->reqid == resp->request_id) 1433 break; 1434 if (listentry == NULL) 1435 return (-1); 1436 1437 LIST_REMOVE(listentry, entries); 1438 listentry->callback(listentry->pdu, resp, listentry->arg); 1439 1440 snmp_client.timeout_stop(listentry->timeout_id); 1441 1442 free(listentry); 1443 return (0); 1444} 1445 1446int 1447snmp_receive(int blocking) 1448{ 1449 int ret; 1450 1451 struct timeval tv; 1452 struct snmp_pdu * resp; 1453 1454 memset(&tv, 0, sizeof(tv)); 1455 1456 resp = malloc(sizeof(struct snmp_pdu)); 1457 if (resp == NULL) { 1458 seterr(&snmp_client, "no memory for returning PDU"); 1459 return (-1) ; 1460 } 1461 1462 if ((ret = snmp_receive_packet(resp, blocking ? NULL : &tv)) <= 0) { 1463 free(resp); 1464 return (ret); 1465 } 1466 ret = snmp_deliver_packet(resp); 1467 snmp_pdu_free(resp); 1468 free(resp); 1469 return (ret); 1470} 1471 1472 1473/* 1474 * Check a GETNEXT response. Here we have three possible outcomes: -1 an 1475 * unexpected error happened. +1 response is ok and is within the table 0 1476 * response is ok, but is behind the table or error is NOSUCHNAME. The req 1477 * should point to a template PDU which contains the base OIDs and the 1478 * syntaxes. This is really only useful to sweep non-sparse tables. 1479 */ 1480static int 1481ok_getnext(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1482{ 1483 u_int i; 1484 1485 if (resp->version != req->version) { 1486 warnx("SNMP GETNEXT: response has wrong version"); 1487 return (-1); 1488 } 1489 1490 if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1491 return (0); 1492 1493 if (resp->error_status != SNMP_ERR_NOERROR) { 1494 warnx("SNMP GETNEXT: error %d", resp->error_status); 1495 return (-1); 1496 } 1497 if (resp->nbindings != req->nbindings) { 1498 warnx("SNMP GETNEXT: bad number of bindings in response"); 1499 return (-1); 1500 } 1501 for (i = 0; i < req->nbindings; i++) { 1502 if (!asn_is_suboid(&req->bindings[i].var, 1503 &resp->bindings[i].var)) { 1504 if (i != 0) 1505 warnx("SNMP GETNEXT: inconsistent table " 1506 "response"); 1507 return (0); 1508 } 1509 if (resp->version != SNMP_V1 && 1510 resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 1511 return (0); 1512 1513 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1514 warnx("SNMP GETNEXT: bad syntax in response"); 1515 return (0); 1516 } 1517 } 1518 return (1); 1519} 1520 1521/* 1522 * Check a GET response. Here we have three possible outcomes: -1 an 1523 * unexpected error happened. +1 response is ok. 0 NOSUCHNAME The req should 1524 * point to a template PDU which contains the OIDs and the syntaxes. This 1525 * is only useful for SNMPv1 or single object GETS. 1526 */ 1527static int 1528ok_get(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1529{ 1530 u_int i; 1531 1532 if (resp->version != req->version) { 1533 warnx("SNMP GET: response has wrong version"); 1534 return (-1); 1535 } 1536 1537 if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1538 return (0); 1539 1540 if (resp->error_status != SNMP_ERR_NOERROR) { 1541 warnx("SNMP GET: error %d", resp->error_status); 1542 return (-1); 1543 } 1544 1545 if (resp->nbindings != req->nbindings) { 1546 warnx("SNMP GET: bad number of bindings in response"); 1547 return (-1); 1548 } 1549 for (i = 0; i < req->nbindings; i++) { 1550 if (asn_compare_oid(&req->bindings[i].var, 1551 &resp->bindings[i].var) != 0) { 1552 warnx("SNMP GET: bad OID in response"); 1553 return (-1); 1554 } 1555 if (snmp_client.version != SNMP_V1 && 1556 (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT || 1557 resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE)) 1558 return (0); 1559 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1560 warnx("SNMP GET: bad syntax in response"); 1561 return (-1); 1562 } 1563 } 1564 return (1); 1565} 1566 1567/*
|
1565 * the number of bindings must be equal in response and request - the 1566 * syntaxes must be the same in response and request - the OIDs must be the 1567 * same in response and request 1568 */ 1569static int 1570ok_set(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1571{ 1572 u_int i; 1573 1574 if (resp->version != req->version) { 1575 warnx("SNMP SET: response has wrong version"); 1576 return (-1); 1577 } 1578 1579 if (resp->error_status == SNMP_ERR_NOSUCHNAME) { 1580 warnx("SNMP SET: error %d", resp->error_status); 1581 return (0); 1582 } 1583 if (resp->error_status != SNMP_ERR_NOERROR) { 1584 warnx("SNMP SET: error %d", resp->error_status); 1585 return (-1); 1586 } 1587 1588 if (resp->nbindings != req->nbindings) { 1589 warnx("SNMP SET: bad number of bindings in response"); 1590 return (-1); 1591 } 1592 for (i = 0; i < req->nbindings; i++) { 1593 if (asn_compare_oid(&req->bindings[i].var, 1594 &resp->bindings[i].var) != 0) { 1595 warnx("SNMP SET: wrong OID in response to SET"); 1596 return (-1); 1597 } 1598 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1599 warnx("SNMP SET: bad syntax in response"); 1600 return (-1); 1601 } 1602 } 1603 return (1); 1604} 1605 1606/* 1607 * Simple checks for response PDUs against request PDUs. Return values: 1=ok, 1608 * 0=nosuchname or similar, -1=failure, -2=no response at all 1609 */ 1610int 1611snmp_pdu_check(const struct snmp_pdu *req, 1612 const struct snmp_pdu *resp) 1613{ 1614 if (resp == NULL) 1615 return (-2); 1616 1617 switch (req->type) { 1618 1619 case SNMP_PDU_GET: 1620 return (ok_get(req, resp)); 1621 1622 case SNMP_PDU_SET: 1623 return (ok_set(req, resp)); 1624 1625 case SNMP_PDU_GETNEXT: 1626 return (ok_getnext(req, resp)); 1627 1628 } 1629 errx(1, "%s: bad pdu type %i", __func__, req->type); 1630} 1631 1632int 1633snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp) 1634{ 1635 u_int i; 1636 int32_t reqid; 1637 int ret; 1638 struct timeval tv = snmp_client.timeout; 1639 struct timeval end; 1640 struct snmp_pdu pdu; 1641 1642 /* 1643 * Make a copy of the request and replace the syntaxes by NULL 1644 * if this is a GET,GETNEXT or GETBULK. 1645 */ 1646 pdu = *req; 1647 if (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GETNEXT || 1648 pdu.type == SNMP_PDU_GETBULK) { 1649 for (i = 0; i < pdu.nbindings; i++) 1650 pdu.bindings[i].syntax = SNMP_SYNTAX_NULL; 1651 } 1652 1653 for (i = 0; i <= snmp_client.retries; i++) { 1654 (void)gettimeofday(&end, NULL); 1655 timeradd(&end, &snmp_client.timeout, &end); 1656 if ((reqid = snmp_send_packet(&pdu)) == -1) 1657 return (-1); 1658 for (;;) { 1659 (void)gettimeofday(&tv, NULL); 1660 if (timercmp(&end, &tv, <=)) 1661 break; 1662 timersub(&end, &tv, &tv); 1663 if ((ret = snmp_receive_packet(resp, &tv)) == 0) 1664 /* timeout */ 1665 break; 1666 1667 if (ret > 0) { 1668 if (reqid == resp->request_id) 1669 return (0); 1670 /* not for us */ 1671 (void)snmp_deliver_packet(resp); 1672 } 1673 if (ret < 0 && errno == EPIPE) 1674 /* stream closed */ 1675 return (-1); 1676 } 1677 } 1678 errno = ETIMEDOUT; 1679 seterr(&snmp_client, "retry count exceeded"); 1680 return (-1); 1681} 1682 1683int 1684snmp_client_set_host(struct snmp_client *cl, const char *h) 1685{ 1686 char *np; 1687 1688 if (h == NULL) { 1689 if (cl->chost != NULL) 1690 free(cl->chost); 1691 cl->chost = NULL; 1692 } else { 1693 if ((np = malloc(strlen(h) + 1)) == NULL) 1694 return (-1); 1695 strcpy(np, h); 1696 if (cl->chost != NULL) 1697 free(cl->chost); 1698 cl->chost = np; 1699 } 1700 return (0); 1701} 1702 1703int 1704snmp_client_set_port(struct snmp_client *cl, const char *p) 1705{ 1706 char *np; 1707 1708 if (p == NULL) { 1709 if (cl->cport != NULL) 1710 free(cl->cport); 1711 cl->cport = NULL; 1712 } else { 1713 if ((np = malloc(strlen(p) + 1)) == NULL) 1714 return (-1); 1715 strcpy(np, p); 1716 if (cl->cport != NULL) 1717 free(cl->cport); 1718 cl->cport = np; 1719 } 1720 return (0); 1721} 1722 1723/* 1724 * parse a server specification 1725 * 1726 * [trans::][community@][server][:port] 1727 */ 1728int 1729snmp_parse_server(struct snmp_client *sc, const char *str) 1730{ 1731 const char *p, *s = str; 1732 1733 /* look for a double colon */ 1734 for (p = s; *p != '\0'; p++) { 1735 if (*p == '\\' && p[1] != '\0') { 1736 p++; 1737 continue; 1738 } 1739 if (*p == ':' && p[1] == ':') 1740 break; 1741 } 1742 if (*p != '\0') { 1743 if (p > s) { 1744 if (p - s == 3 && strncmp(s, "udp", 3) == 0) 1745 sc->trans = SNMP_TRANS_UDP; 1746 else if (p - s == 6 && strncmp(s, "stream", 6) == 0) 1747 sc->trans = SNMP_TRANS_LOC_STREAM; 1748 else if (p - s == 5 && strncmp(s, "dgram", 5) == 0) 1749 sc->trans = SNMP_TRANS_LOC_DGRAM; 1750 else { 1751 seterr(sc, "unknown SNMP transport '%.*s'", 1752 (int)(p - s), s); 1753 return (-1); 1754 } 1755 } 1756 s = p + 2; 1757 } 1758 1759 /* look for a @ */ 1760 for (p = s; *p != '\0'; p++) { 1761 if (*p == '\\' && p[1] != '\0') { 1762 p++; 1763 continue; 1764 } 1765 if (*p == '@') 1766 break; 1767 } 1768 1769 if (*p != '\0') { 1770 if (p - s > SNMP_COMMUNITY_MAXLEN) { 1771 seterr(sc, "community string too long"); 1772 return (-1); 1773 } 1774 strncpy(sc->read_community, s, p - s); 1775 sc->read_community[p - s] = '\0'; 1776 strncpy(sc->write_community, s, p - s); 1777 sc->write_community[p - s] = '\0'; 1778 s = p + 1; 1779 } 1780 1781 /* look for a colon */ 1782 for (p = s; *p != '\0'; p++) { 1783 if (*p == '\\' && p[1] != '\0') { 1784 p++; 1785 continue; 1786 } 1787 if (*p == ':') 1788 break; 1789 } 1790 1791 if (*p == ':') { 1792 if (p > s) { 1793 /* host:port */ 1794 free(sc->chost); 1795 if ((sc->chost = malloc(p - s + 1)) == NULL) { 1796 seterr(sc, "%s", strerror(errno)); 1797 return (-1); 1798 } 1799 strncpy(sc->chost, s, p - s); 1800 sc->chost[p - s] = '\0'; 1801 } 1802 /* port */ 1803 free(sc->cport); 1804 if ((sc->cport = malloc(strlen(p + 1) + 1)) == NULL) { 1805 seterr(sc, "%s", strerror(errno)); 1806 return (-1); 1807 } 1808 strcpy(sc->cport, p + 1); 1809 1810 } else if (p > s) { 1811 /* host */ 1812 free(sc->chost); 1813 if ((sc->chost = malloc(strlen(s) + 1)) == NULL) { 1814 seterr(sc, "%s", strerror(errno)); 1815 return (-1); 1816 } 1817 strcpy(sc->chost, s); 1818 } 1819 return (0); 1820}
| 1569 * the number of bindings must be equal in response and request - the 1570 * syntaxes must be the same in response and request - the OIDs must be the 1571 * same in response and request 1572 */ 1573static int 1574ok_set(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1575{ 1576 u_int i; 1577 1578 if (resp->version != req->version) { 1579 warnx("SNMP SET: response has wrong version"); 1580 return (-1); 1581 } 1582 1583 if (resp->error_status == SNMP_ERR_NOSUCHNAME) { 1584 warnx("SNMP SET: error %d", resp->error_status); 1585 return (0); 1586 } 1587 if (resp->error_status != SNMP_ERR_NOERROR) { 1588 warnx("SNMP SET: error %d", resp->error_status); 1589 return (-1); 1590 } 1591 1592 if (resp->nbindings != req->nbindings) { 1593 warnx("SNMP SET: bad number of bindings in response"); 1594 return (-1); 1595 } 1596 for (i = 0; i < req->nbindings; i++) { 1597 if (asn_compare_oid(&req->bindings[i].var, 1598 &resp->bindings[i].var) != 0) { 1599 warnx("SNMP SET: wrong OID in response to SET"); 1600 return (-1); 1601 } 1602 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1603 warnx("SNMP SET: bad syntax in response"); 1604 return (-1); 1605 } 1606 } 1607 return (1); 1608} 1609 1610/* 1611 * Simple checks for response PDUs against request PDUs. Return values: 1=ok, 1612 * 0=nosuchname or similar, -1=failure, -2=no response at all 1613 */ 1614int 1615snmp_pdu_check(const struct snmp_pdu *req, 1616 const struct snmp_pdu *resp) 1617{ 1618 if (resp == NULL) 1619 return (-2); 1620 1621 switch (req->type) { 1622 1623 case SNMP_PDU_GET: 1624 return (ok_get(req, resp)); 1625 1626 case SNMP_PDU_SET: 1627 return (ok_set(req, resp)); 1628 1629 case SNMP_PDU_GETNEXT: 1630 return (ok_getnext(req, resp)); 1631 1632 } 1633 errx(1, "%s: bad pdu type %i", __func__, req->type); 1634} 1635 1636int 1637snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp) 1638{ 1639 u_int i; 1640 int32_t reqid; 1641 int ret; 1642 struct timeval tv = snmp_client.timeout; 1643 struct timeval end; 1644 struct snmp_pdu pdu; 1645 1646 /* 1647 * Make a copy of the request and replace the syntaxes by NULL 1648 * if this is a GET,GETNEXT or GETBULK. 1649 */ 1650 pdu = *req; 1651 if (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GETNEXT || 1652 pdu.type == SNMP_PDU_GETBULK) { 1653 for (i = 0; i < pdu.nbindings; i++) 1654 pdu.bindings[i].syntax = SNMP_SYNTAX_NULL; 1655 } 1656 1657 for (i = 0; i <= snmp_client.retries; i++) { 1658 (void)gettimeofday(&end, NULL); 1659 timeradd(&end, &snmp_client.timeout, &end); 1660 if ((reqid = snmp_send_packet(&pdu)) == -1) 1661 return (-1); 1662 for (;;) { 1663 (void)gettimeofday(&tv, NULL); 1664 if (timercmp(&end, &tv, <=)) 1665 break; 1666 timersub(&end, &tv, &tv); 1667 if ((ret = snmp_receive_packet(resp, &tv)) == 0) 1668 /* timeout */ 1669 break; 1670 1671 if (ret > 0) { 1672 if (reqid == resp->request_id) 1673 return (0); 1674 /* not for us */ 1675 (void)snmp_deliver_packet(resp); 1676 } 1677 if (ret < 0 && errno == EPIPE) 1678 /* stream closed */ 1679 return (-1); 1680 } 1681 } 1682 errno = ETIMEDOUT; 1683 seterr(&snmp_client, "retry count exceeded"); 1684 return (-1); 1685} 1686 1687int 1688snmp_client_set_host(struct snmp_client *cl, const char *h) 1689{ 1690 char *np; 1691 1692 if (h == NULL) { 1693 if (cl->chost != NULL) 1694 free(cl->chost); 1695 cl->chost = NULL; 1696 } else { 1697 if ((np = malloc(strlen(h) + 1)) == NULL) 1698 return (-1); 1699 strcpy(np, h); 1700 if (cl->chost != NULL) 1701 free(cl->chost); 1702 cl->chost = np; 1703 } 1704 return (0); 1705} 1706 1707int 1708snmp_client_set_port(struct snmp_client *cl, const char *p) 1709{ 1710 char *np; 1711 1712 if (p == NULL) { 1713 if (cl->cport != NULL) 1714 free(cl->cport); 1715 cl->cport = NULL; 1716 } else { 1717 if ((np = malloc(strlen(p) + 1)) == NULL) 1718 return (-1); 1719 strcpy(np, p); 1720 if (cl->cport != NULL) 1721 free(cl->cport); 1722 cl->cport = np; 1723 } 1724 return (0); 1725} 1726 1727/* 1728 * parse a server specification 1729 * 1730 * [trans::][community@][server][:port] 1731 */ 1732int 1733snmp_parse_server(struct snmp_client *sc, const char *str) 1734{ 1735 const char *p, *s = str; 1736 1737 /* look for a double colon */ 1738 for (p = s; *p != '\0'; p++) { 1739 if (*p == '\\' && p[1] != '\0') { 1740 p++; 1741 continue; 1742 } 1743 if (*p == ':' && p[1] == ':') 1744 break; 1745 } 1746 if (*p != '\0') { 1747 if (p > s) { 1748 if (p - s == 3 && strncmp(s, "udp", 3) == 0) 1749 sc->trans = SNMP_TRANS_UDP; 1750 else if (p - s == 6 && strncmp(s, "stream", 6) == 0) 1751 sc->trans = SNMP_TRANS_LOC_STREAM; 1752 else if (p - s == 5 && strncmp(s, "dgram", 5) == 0) 1753 sc->trans = SNMP_TRANS_LOC_DGRAM; 1754 else { 1755 seterr(sc, "unknown SNMP transport '%.*s'", 1756 (int)(p - s), s); 1757 return (-1); 1758 } 1759 } 1760 s = p + 2; 1761 } 1762 1763 /* look for a @ */ 1764 for (p = s; *p != '\0'; p++) { 1765 if (*p == '\\' && p[1] != '\0') { 1766 p++; 1767 continue; 1768 } 1769 if (*p == '@') 1770 break; 1771 } 1772 1773 if (*p != '\0') { 1774 if (p - s > SNMP_COMMUNITY_MAXLEN) { 1775 seterr(sc, "community string too long"); 1776 return (-1); 1777 } 1778 strncpy(sc->read_community, s, p - s); 1779 sc->read_community[p - s] = '\0'; 1780 strncpy(sc->write_community, s, p - s); 1781 sc->write_community[p - s] = '\0'; 1782 s = p + 1; 1783 } 1784 1785 /* look for a colon */ 1786 for (p = s; *p != '\0'; p++) { 1787 if (*p == '\\' && p[1] != '\0') { 1788 p++; 1789 continue; 1790 } 1791 if (*p == ':') 1792 break; 1793 } 1794 1795 if (*p == ':') { 1796 if (p > s) { 1797 /* host:port */ 1798 free(sc->chost); 1799 if ((sc->chost = malloc(p - s + 1)) == NULL) { 1800 seterr(sc, "%s", strerror(errno)); 1801 return (-1); 1802 } 1803 strncpy(sc->chost, s, p - s); 1804 sc->chost[p - s] = '\0'; 1805 } 1806 /* port */ 1807 free(sc->cport); 1808 if ((sc->cport = malloc(strlen(p + 1) + 1)) == NULL) { 1809 seterr(sc, "%s", strerror(errno)); 1810 return (-1); 1811 } 1812 strcpy(sc->cport, p + 1); 1813 1814 } else if (p > s) { 1815 /* host */ 1816 free(sc->chost); 1817 if ((sc->chost = malloc(strlen(s) + 1)) == NULL) { 1818 seterr(sc, "%s", strerror(errno)); 1819 return (-1); 1820 } 1821 strcpy(sc->chost, s); 1822 } 1823 return (0); 1824}
|