snmp_pdu.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:16k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /*
  2.  * SNMP PDU Encoding
  3.  *
  4.  * Complies with:
  5.  *
  6.  * RFC 1902: Structure of Management Information for SNMPv2
  7.  *
  8.  */
  9. /**********************************************************************
  10.  *
  11.  *           Copyright 1997 by Carnegie Mellon University
  12.  * 
  13.  *                       All Rights Reserved
  14.  * 
  15.  * Permission to use, copy, modify, and distribute this software and its
  16.  * documentation for any purpose and without fee is hereby granted,
  17.  * provided that the above copyright notice appear in all copies and that
  18.  * both that copyright notice and this permission notice appear in
  19.  * supporting documentation, and that the name of CMU not be
  20.  * used in advertising or publicity pertaining to distribution of the
  21.  * software without specific, written prior permission.
  22.  * 
  23.  * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  24.  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  25.  * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  26.  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  27.  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  28.  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  29.  * SOFTWARE.
  30.  * 
  31.  * Author: Ryan Troll <ryan+@andrew.cmu.edu>
  32.  * 
  33.  **********************************************************************/
  34. #include "config.h"
  35. #include <stdio.h>
  36. #if HAVE_UNISTD_H
  37. #include <unistd.h>
  38. #endif
  39. #if HAVE_STDLIB_H
  40. #include <stdlib.h>
  41. #endif
  42. #if HAVE_SYS_TYPES_H
  43. #include <sys/types.h>
  44. #endif
  45. #if HAVE_CTYPE_H
  46. #include <ctype.h>
  47. #endif
  48. #if HAVE_GNUMALLOC_H
  49. #include <gnumalloc.h>
  50. #elif HAVE_MALLOC_H && !defined(_SQUID_FREEBSD_) && !defined(_SQUID_NEXT_)
  51. #include <malloc.h>
  52. #endif
  53. #if HAVE_MEMORY_H
  54. #include <memory.h>
  55. #endif
  56. #ifdef HAVE_STRING_H
  57. #include <string.h>
  58. #endif
  59. #ifdef HAVE_STRINGS_H
  60. #include <strings.h>
  61. #endif
  62. #if HAVE_BSTRING_H
  63. #include <bstring.h>
  64. #endif
  65. #if HAVE_SYS_SOCKET_H
  66. #include <sys/socket.h>
  67. #endif
  68. #if HAVE_NETINET_IN_H
  69. #include <netinet/in.h>
  70. #endif
  71. #if HAVE_ARPA_INET_H
  72. #include <arpa/inet.h>
  73. #endif
  74. #if HAVE_SYS_TIME_H
  75. #include <sys/time.h>
  76. #endif
  77. #if HAVE_NETDB_H
  78. #include <netdb.h>
  79. #endif
  80. #include "snmp.h"
  81. #include "asn1.h"
  82. #include "snmp_error.h"
  83. #include "snmp_vars.h"
  84. #include "snmp_pdu.h"
  85. #include "snmp_msg.h"
  86. #include "snmp_api_error.h"
  87. #include "util.h"
  88. /* #define DEBUG_PDU 1 */
  89. /* #define DEBUG_PDU_DECODE 1 */
  90. /* #define DEBUG_PDU_ENCODE 1 */
  91. #define ASN_PARSE_ERROR(x) {  return(x); }
  92. /**********************************************************************/
  93. /* Create a PDU.
  94.  */
  95. struct snmp_pdu *
  96. snmp_pdu_create(int command)
  97. {
  98.     struct snmp_pdu *pdu;
  99. #ifdef DEBUG_PDU
  100.     snmplib_debug(8, "PDU:  Creatingn");
  101. #endif
  102.     pdu = (struct snmp_pdu *) xmalloc(sizeof(struct snmp_pdu));
  103.     if (pdu == NULL) {
  104. snmp_set_api_error(SNMPERR_OS_ERR);
  105. return (NULL);
  106.     }
  107.     memset((char *) pdu, '', sizeof(struct snmp_pdu));
  108.     pdu->command = command;
  109.     pdu->errstat = SNMP_DEFAULT_ERRSTAT;
  110.     pdu->errindex = SNMP_DEFAULT_ERRINDEX;
  111.     pdu->address.sin_addr.s_addr = SNMP_DEFAULT_ADDRESS;
  112.     pdu->enterprise = NULL;
  113.     pdu->enterprise_length = 0;
  114.     pdu->variables = NULL;
  115. #ifdef DEBUG_PDU
  116.     snmplib_debug(8, "PDU:  Created %xn", (unsigned int) pdu);
  117. #endif
  118.     return (pdu);
  119. }
  120. /**********************************************************************/
  121. /* Clone an existing PDU.
  122.  */
  123. struct snmp_pdu *
  124. snmp_pdu_clone(struct snmp_pdu *Src)
  125. {
  126.     struct snmp_pdu *Dest;
  127. #ifdef DEBUG_PDU
  128.     snmplib_debug(8, "PDU %x:  Cloningn", (unsigned int) Src);
  129. #endif
  130.     Dest = (struct snmp_pdu *) xmalloc(sizeof(struct snmp_pdu));
  131.     if (Dest == NULL) {
  132. snmp_set_api_error(SNMPERR_OS_ERR);
  133. return (NULL);
  134.     }
  135.     xmemcpy((char *) Dest, (char *) Src, sizeof(struct snmp_pdu));
  136. #ifdef DEBUG_PDU
  137.     snmplib_debug(8, "PDU %x:  Created %xn", (unsigned int) Src, (unsigned int) Dest);
  138. #endif
  139.     return (Dest);
  140. }
  141. /**********************************************************************/
  142. /*
  143.  * If there was an error in the input pdu, creates a clone of the pdu
  144.  * that includes all the variables except the one marked by the errindex.
  145.  * The command is set to the input command and the reqid, errstat, and
  146.  * errindex are set to default values.
  147.  * If the error status didn't indicate an error, the error index didn't
  148.  * indicate a variable, the pdu wasn't a get response message, or there
  149.  * would be no remaining variables, this function will return NULL.
  150.  * If everything was successful, a pointer to the fixed cloned pdu will
  151.  * be returned.
  152.  */
  153. struct snmp_pdu *
  154. snmp_pdu_fix(struct snmp_pdu *pdu, int command)
  155. {
  156.     return (snmp_fix_pdu(pdu, command));
  157. }
  158. struct snmp_pdu *
  159. snmp_fix_pdu(struct snmp_pdu *pdu, int command)
  160. {
  161.     struct variable_list *var, *newvar;
  162.     struct snmp_pdu *newpdu;
  163.     int index;
  164.     int copied = 0;
  165. #ifdef DEBUG_PDU
  166.     snmplib_debug(8, "PDU %x:  Fixing.  Err index is %dn",
  167. (unsigned int) pdu, (unsigned int) pdu->errindex);
  168. #endif
  169.     if (pdu->command != SNMP_PDU_RESPONSE ||
  170. pdu->errstat == SNMP_ERR_NOERROR ||
  171. pdu->errindex <= 0) {
  172. snmp_set_api_error(SNMPERR_UNABLE_TO_FIX);
  173. return (NULL);
  174.     }
  175.     /* clone the pdu */
  176.     newpdu = snmp_pdu_clone(pdu);
  177.     if (newpdu == NULL)
  178. return (NULL);
  179.     newpdu->variables = 0;
  180.     newpdu->command = command;
  181.     newpdu->reqid = SNMP_DEFAULT_REQID;
  182.     newpdu->errstat = SNMP_DEFAULT_ERRSTAT;
  183.     newpdu->errindex = SNMP_DEFAULT_ERRINDEX;
  184.     /* Loop through the variables, removing whatever isn't necessary */
  185.     var = pdu->variables;
  186.     index = 1;
  187.     /* skip first variable if necessary */
  188.     if (pdu->errindex == index) {
  189. var = var->next_variable;
  190. index++;
  191.     }
  192.     if (var != NULL) {
  193. /* VAR is the first uncopied variable */
  194. /* Clone this variable */
  195. newpdu->variables = snmp_var_clone(var);
  196. if (newpdu->variables == NULL) {
  197.     snmp_pdu_free(newpdu);
  198.     return (NULL);
  199. }
  200. copied++;
  201. newvar = newpdu->variables;
  202. /* VAR has been copied to NEWVAR. */
  203. while (var->next_variable) {
  204.     /* Skip the item that was bad */
  205.     if (++index == pdu->errindex) {
  206. var = var->next_variable;
  207. continue;
  208.     }
  209.     /* Copy this var */
  210.     newvar->next_variable = snmp_var_clone(var->next_variable);
  211.     if (newvar->next_variable == NULL) {
  212. snmp_pdu_free(newpdu);
  213. return (NULL);
  214.     }
  215.     /* Move to the next one */
  216.     newvar = newvar->next_variable;
  217.     var = var->next_variable;
  218.     copied++;
  219. }
  220. newvar->next_variable = NULL;
  221.     }
  222.     /* If we didn't copy anything, free the new pdu. */
  223.     if (index < pdu->errindex || copied == 0) {
  224. snmp_free_pdu(newpdu);
  225. snmp_set_api_error(SNMPERR_UNABLE_TO_FIX);
  226. return (NULL);
  227.     }
  228. #ifdef DEBUG_PDU
  229.     snmplib_debug(8, "PDU %x:  Fixed PDU is %xn",
  230. (unsigned int) pdu, (unsigned int) newpdu);
  231. #endif
  232.     return (newpdu);
  233. }
  234. /**********************************************************************/
  235. void 
  236. snmp_pdu_free(struct snmp_pdu *pdu)
  237. {
  238.     snmp_free_pdu(pdu);
  239. }
  240. /*
  241.  * Frees the pdu and any xmalloc'd data associated with it.
  242.  */
  243. void 
  244. snmp_free_pdu(struct snmp_pdu *pdu)
  245. {
  246.     struct variable_list *vp, *ovp;
  247.     vp = pdu->variables;
  248.     while (vp) {
  249. ovp = vp;
  250. vp = vp->next_variable;
  251. snmp_var_free(ovp);
  252.     }
  253.     if (pdu->enterprise)
  254. xfree((char *) pdu->enterprise);
  255.     xfree((char *) pdu);
  256. }
  257. /**********************************************************************/
  258. /* Encode this PDU into DestBuf.
  259.  *
  260.  * Returns a pointer to the next byte in the buffer (where the Variable
  261.  * Bindings belong.)
  262.  */
  263. /*
  264.  * RFC 1902: Structure of Management Information for SNMPv2
  265.  *
  266.  *   PDU ::= 
  267.  *    SEQUENCE {
  268.  *      request-id   INTEGER32
  269.  *      error-status INTEGER
  270.  *      error-index  INTEGER
  271.  *      Variable Bindings
  272.  *    }
  273.  *
  274.  * BulkPDU ::=
  275.  *    SEQUENCE {
  276.  *      request-id      INTEGER32
  277.  *      non-repeaters   INTEGER
  278.  *      max-repetitions INTEGER
  279.  *      Variable Bindings
  280.  *    }
  281.  */
  282. /*
  283.  * RFC 1157: A Simple Network Management Protocol (SNMP)
  284.  *
  285.  *   PDU ::= 
  286.  *    SEQUENCE {
  287.  *      request-id   INTEGER
  288.  *      error-status INTEGER
  289.  *      error-index  INTEGER
  290.  *      Variable Bindings
  291.  *    }
  292.  *
  293.  *   TrapPDU ::=
  294.  *    SEQUENCE {
  295.  *      enterprise    NetworkAddress
  296.  *      generic-trap  INTEGER
  297.  *      specific-trap INTEGER
  298.  *      time-stamp    TIMETICKS
  299.  *      Variable Bindings
  300.  *    }
  301.  */
  302. u_char *
  303. snmp_pdu_encode(u_char * DestBuf, int *DestBufLen,
  304.     struct snmp_pdu *PDU)
  305. {
  306.     u_char *bufp;
  307. #ifdef DEBUG_PDU_ENCODE
  308.     snmplib_debug(8, "PDU: Encoding %dn", PDU->command);
  309. #endif
  310.     /* ASN.1 Header */
  311.     switch (PDU->command) {
  312. /**********************************************************************/
  313.     case TRP_REQ_MSG:
  314. /* SNMPv1 Trap */
  315. /* enterprise */
  316. bufp = asn_build_objid(DestBuf, DestBufLen,
  317.     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
  318.     (oid *) PDU->enterprise, PDU->enterprise_length);
  319. if (bufp == NULL)
  320.     return (NULL);
  321. /* agent-addr */
  322. bufp = asn_build_string(bufp, DestBufLen,
  323.     (u_char) (SMI_IPADDRESS | ASN_PRIMITIVE),
  324.     (u_char *) & PDU->agent_addr.sin_addr.s_addr,
  325.     sizeof(PDU->agent_addr.sin_addr.s_addr));
  326. if (bufp == NULL)
  327.     return (NULL);
  328. /* generic trap */
  329. bufp = asn_build_int(bufp, DestBufLen,
  330.     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  331.     (int *) &PDU->trap_type, sizeof(PDU->trap_type));
  332. if (bufp == NULL)
  333.     return (NULL);
  334. /* specific trap */
  335. bufp = asn_build_int(bufp, DestBufLen,
  336.     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  337.     (int *) &PDU->specific_type,
  338.     sizeof(PDU->specific_type));
  339. if (bufp == NULL)
  340.     return (NULL);
  341. /* timestamp */
  342. bufp = asn_build_unsigned_int(bufp, DestBufLen,
  343.     (u_char) (SMI_TIMETICKS | ASN_PRIMITIVE),
  344.     &PDU->time, sizeof(PDU->time));
  345. if (bufp == NULL)
  346.     return (NULL);
  347. break;
  348. /**********************************************************************/
  349.     case SNMP_PDU_GETBULK:
  350. /* SNMPv2 Bulk Request */
  351. /* request id */
  352. bufp = asn_build_int(DestBuf, DestBufLen,
  353.     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  354.     &PDU->reqid, sizeof(PDU->reqid));
  355. if (bufp == NULL)
  356.     return (NULL);
  357. /* non-repeaters */
  358. bufp = asn_build_int(bufp, DestBufLen,
  359.     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  360.     &PDU->non_repeaters,
  361.     sizeof(PDU->non_repeaters));
  362. if (bufp == NULL)
  363.     return (NULL);
  364. /* max-repetitions */
  365. bufp = asn_build_int(bufp, DestBufLen,
  366.     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  367.     &PDU->max_repetitions,
  368.     sizeof(PDU->max_repetitions));
  369. if (bufp == NULL)
  370.     return (NULL);
  371. break;
  372. /**********************************************************************/
  373.     default:
  374. /* Normal PDU Encoding */
  375. /* request id */
  376. #ifdef DEBUG_PDU_ENCODE
  377. snmplib_debug(8, "PDU: Request ID %d (0x%x)n", PDU->reqid, DestBuf);
  378. #endif
  379. bufp = asn_build_int(DestBuf, DestBufLen,
  380.     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  381.     &PDU->reqid, sizeof(PDU->reqid));
  382. if (bufp == NULL)
  383.     return (NULL);
  384. /* error status */
  385. #ifdef DEBUG_PDU_ENCODE
  386. snmplib_debug(8, "PDU: Error Status %d (0x%x)n", PDU->errstat, bufp);
  387. #endif
  388. bufp = asn_build_int(bufp, DestBufLen,
  389.     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  390.     &PDU->errstat, sizeof(PDU->errstat));
  391. if (bufp == NULL)
  392.     return (NULL);
  393. /* error index */
  394. #ifdef DEBUG_PDU_ENCODE
  395. snmplib_debug(8, "PDU: Error index %d (0x%x)n", PDU->errindex, bufp);
  396. #endif
  397. bufp = asn_build_int(bufp, DestBufLen,
  398.     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  399.     &PDU->errindex, sizeof(PDU->errindex));
  400. if (bufp == NULL)
  401.     return (NULL);
  402. break;
  403.     } /* End of encoding */
  404.     return (bufp);
  405. }
  406. /**********************************************************************/
  407. /* Decodes PDU from Packet into PDU.
  408.  *
  409.  * Returns a pointer to the next byte of the packet, which is where the
  410.  * Variable Bindings start.
  411.  */
  412. u_char *
  413. snmp_pdu_decode(u_char * Packet, /* data */
  414.     int *Length, /* &length */
  415.     struct snmp_pdu * PDU)
  416. { /* pdu */
  417.     u_char *bufp;
  418.     u_char PDUType;
  419.     int four;
  420.     u_char ASNType;
  421.     oid objid[MAX_NAME_LEN];
  422.     bufp = asn_parse_header(Packet, Length, &PDUType);
  423.     if (bufp == NULL)
  424. ASN_PARSE_ERROR(NULL);
  425. #ifdef DEBUG_PDU_DECODE
  426.     snmplib_debug(8, "PDU Type: %dn", PDUType);
  427. #endif
  428.     PDU->command = PDUType;
  429.     switch (PDUType) {
  430.     case TRP_REQ_MSG:
  431. /* SNMPv1 Trap Message */
  432. /* enterprise */
  433. PDU->enterprise_length = MAX_NAME_LEN;
  434. bufp = asn_parse_objid(bufp, Length,
  435.     &ASNType, objid, &PDU->enterprise_length);
  436. if (bufp == NULL)
  437.     ASN_PARSE_ERROR(NULL);
  438. PDU->enterprise = (oid *) xmalloc(PDU->enterprise_length * sizeof(oid));
  439. if (PDU->enterprise == NULL) {
  440.     snmp_set_api_error(SNMPERR_OS_ERR);
  441.     return (NULL);
  442. }
  443. xmemcpy((char *) PDU->enterprise, (char *) objid,
  444.     PDU->enterprise_length * sizeof(oid));
  445. /* Agent-addr */
  446. four = 4;
  447. bufp = asn_parse_string(bufp, Length,
  448.     &ASNType,
  449.     (u_char *) & PDU->agent_addr.sin_addr.s_addr,
  450.     &four);
  451. if (bufp == NULL)
  452.     ASN_PARSE_ERROR(NULL);
  453. /* Generic trap */
  454. bufp = asn_parse_int(bufp, Length,
  455.     &ASNType,
  456.     (int *) &PDU->trap_type,
  457.     sizeof(PDU->trap_type));
  458. if (bufp == NULL)
  459.     ASN_PARSE_ERROR(NULL);
  460. /* Specific Trap */
  461. bufp = asn_parse_int(bufp, Length,
  462.     &ASNType,
  463.     (int *) &PDU->specific_type,
  464.     sizeof(PDU->specific_type));
  465. if (bufp == NULL)
  466.     ASN_PARSE_ERROR(NULL);
  467. /* Timestamp */
  468. bufp = asn_parse_unsigned_int(bufp, Length,
  469.     &ASNType,
  470.     &PDU->time, sizeof(PDU->time));
  471. if (bufp == NULL)
  472.     ASN_PARSE_ERROR(NULL);
  473. break;
  474. /**********************************************************************/
  475.     case SNMP_PDU_GETBULK:
  476. /* SNMPv2 Bulk Request */
  477. /* request id */
  478. bufp = asn_parse_int(bufp, Length,
  479.     &ASNType,
  480.     &PDU->reqid, sizeof(PDU->reqid));
  481. if (bufp == NULL)
  482.     ASN_PARSE_ERROR(NULL);
  483. /* non-repeaters */
  484. bufp = asn_parse_int(bufp, Length,
  485.     &ASNType,
  486.     &PDU->non_repeaters, sizeof(PDU->non_repeaters));
  487. if (bufp == NULL)
  488.     ASN_PARSE_ERROR(NULL);
  489. /* max-repetitions */
  490. bufp = asn_parse_int(bufp, Length,
  491.     &ASNType,
  492.     &PDU->max_repetitions, sizeof(PDU->max_repetitions));
  493. if (bufp == NULL)
  494.     ASN_PARSE_ERROR(NULL);
  495. break;
  496. /**********************************************************************/
  497.     default:
  498. /* Normal PDU Encoding */
  499. /* request id */
  500. bufp = asn_parse_int(bufp, Length,
  501.     &ASNType,
  502.     &PDU->reqid, sizeof(PDU->reqid));
  503. if (bufp == NULL)
  504.     ASN_PARSE_ERROR(NULL);
  505. #ifdef DEBUG_PDU_DECODE
  506. snmplib_debug(8, "PDU Request ID: %dn", PDU->reqid);
  507. #endif
  508. /* error status */
  509. bufp = asn_parse_int(bufp, Length,
  510.     &ASNType,
  511.     &PDU->errstat, sizeof(PDU->errstat));
  512. if (bufp == NULL)
  513.     ASN_PARSE_ERROR(NULL);
  514. #ifdef DEBUG_PDU_DECODE
  515. snmplib_debug(8, "PDU Error Status: %dn", PDU->errstat);
  516. #endif
  517. /* error index */
  518. bufp = asn_parse_int(bufp, Length,
  519.     &ASNType,
  520.     &PDU->errindex, sizeof(PDU->errindex));
  521. if (bufp == NULL)
  522.     ASN_PARSE_ERROR(NULL);
  523. #ifdef DEBUG_PDU_DECODE
  524. snmplib_debug(8, "PDU Error Index: %dn", PDU->errindex);
  525. #endif
  526. break;
  527.     }
  528.     return (bufp);
  529. }
  530. char *
  531. snmp_pdu_type(struct snmp_pdu *PDU)
  532. {
  533.     switch (PDU->command) {
  534.     case SNMP_PDU_GET:
  535. return ("GET");
  536. break;
  537.     case SNMP_PDU_GETNEXT:
  538. return ("GETNEXT");
  539. break;
  540.     case SNMP_PDU_RESPONSE:
  541. return ("RESPONSE");
  542. break;
  543.     case SNMP_PDU_SET:
  544. return ("SET");
  545. break;
  546.     case SNMP_PDU_GETBULK:
  547. return ("GETBULK");
  548. break;
  549.     case SNMP_PDU_INFORM:
  550. return ("INFORM");
  551. break;
  552.     case SNMP_PDU_V2TRAP:
  553. return ("V2TRAP");
  554. break;
  555.     case SNMP_PDU_REPORT:
  556. return ("REPORT");
  557. break;
  558.     case TRP_REQ_MSG:
  559. return ("V1TRAP");
  560. break;
  561.     default:
  562. return ("Unknown");
  563. break;
  564.     }
  565. }
  566. /*
  567.  * Add a null variable with the requested name to the end of the list of
  568.  * variables for this pdu.
  569.  */
  570. void 
  571. snmp_add_null_var(struct snmp_pdu *pdu, oid * name, int name_length)
  572. {
  573.     struct variable_list *vars;
  574.     struct variable_list *ptr;
  575.     vars = snmp_var_new(name, name_length);
  576.     if (vars == NULL) {
  577. perror("snmp_add_null_var:xmalloc");
  578. return;
  579.     }
  580.     if (pdu->variables == NULL) {
  581. pdu->variables = vars;
  582.     } else {
  583. /* Insert at the end */
  584. for (ptr = pdu->variables;
  585.     ptr->next_variable;
  586.     ptr = ptr->next_variable)
  587.     /*EXIT */ ;
  588. ptr->next_variable = vars;
  589.     }
  590.     return;
  591. }