Mercurial > hg > freeDiameter
changeset 706:4ffbc9f1e922
Large UNTESTED commit with the following changes:
* Improved DiameterIdentity handling (esp. interationalization issues),
and improve efficiency of some string operations in peers, sessions,
and dictionary modules (closes #7)
* Cleanup in the session module to free only unreferenced sessions (#16)
* Removed fd_cpu_flush_cache(), replaced by more robust alternatives.
* Improved peer state machine algorithm to counter SCTP multistream race
condition.
line wrap: on
line diff
--- a/contrib/debian/changelog Mon Jan 31 17:22:21 2011 +0900 +++ b/contrib/debian/changelog Wed Feb 09 15:26:58 2011 +0900 @@ -4,7 +4,7 @@ framework now contained in the libfdcore library. libfreeDiameter renamed as libfdproto. This closes #15. - * API version bumped to version 4 accordingly. + * API version bumped to version 4. * Improved fd_dict_new() when the same object already exists. * Improvements to dict_legacy_xml extension. * Cleanups in links to shared libraries @@ -12,8 +12,15 @@ to RADIUS (closes #25) * Allow default signals such as SIGTSTP (ctrl-z) to be used with freeDiameterd + * Improved DiameterIdentity handling (esp. interationalization issues), + and improve efficiency of some string operations in peers, sessions, + and dictionary modules (closes #7) + * Cleanup in the session module to free only unreferenced sessions (#16) + * Removed fd_cpu_flush_cache(), replaced by more robust alternatives. + * Improved peer state machine algorithm to counter SCTP multistream race + condition. - -- Sebastien Decugis <sdecugis@nict.go.jp> Tue, 18 Jan 2011 15:13:00 +0900 + -- Sebastien Decugis <sdecugis@nict.go.jp> Mon, 07 Feb 2011 17:24:20 +0900 freediameter (1.0.4) UNRELEASED; urgency=low
--- a/doc/dbg_interactive.py.sample Mon Jan 31 17:22:21 2011 +0900 +++ b/doc/dbg_interactive.py.sample Wed Feb 09 15:26:58 2011 +0900 @@ -104,7 +104,7 @@ ############# Hash ############ -hex(fd_hash("hello world")) # It accepts binary data +hex(fd_os_hash("hello world")) # It accepts binary data ############# Dictionary ############
--- a/doc/freediameter.conf.sample Mon Jan 31 17:22:21 2011 +0900 +++ b/doc/freediameter.conf.sample Wed Feb 09 15:26:58 2011 +0900 @@ -210,6 +210,7 @@ # ConnectTo = "202.249.37.5"; # ConnectTo = "2001:200:903:2::202:1"; # TLS_Prio = "NORMAL"; +# Realm = "realm.net"; # Reject the peer if it does not advertise this realm. # Examples: #ConnectPeer = "aaa.wide.ad.jp"; #ConnectPeer = "old.diameter.serv" { TcTimer = 60; TLS_old_method; No_SCTP; } ;
--- a/extensions/app_radgw/rgw_clients.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/app_radgw/rgw_clients.c Wed Feb 09 15:26:58 2011 +0900 @@ -85,10 +85,14 @@ /* The FQDN, realm, and optional aliases */ int is_local; /* true if the RADIUS client runs on the same host -- we use Diameter Identity in that case */ enum rgw_cli_type type; /* is it a proxy ? */ - char *fqdn; + DiamId_t fqdn; /* malloc'd here */ size_t fqdn_len; - char *realm; - char **aliases; + DiamId_t realm; /* references another string, do not free */ + size_t realm_len; + struct { + os0_t name; + size_t len; + } *aliases; /* Received aliases */ size_t aliases_nb; /* The secret key data. */ @@ -209,7 +213,8 @@ static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type ) { struct rgw_client *tmp = NULL; - char buf[255]; + DiamId_t fqdn; + size_t fqdn_len; int ret, i; int loc = 0; @@ -219,6 +224,7 @@ /* The client is local */ loc = 1; } else { + char buf[255]; /* Search FQDN for the client */ ret = getnameinfo( *ip_port, sizeof(struct sockaddr_storage), &buf[0], sizeof(buf), NULL, 0, 0 ); @@ -226,6 +232,12 @@ TRACE_DEBUG(INFO, "Unable to resolve peer name: %s", gai_strerror(ret)); return EINVAL; } + fqdn = &buf[0]; + CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&fqdn, &fqdn_len, 1), + { + TRACE_DEBUG(INFO, "Unable to use resolved peer name '%s' as DiameterIdentity: %s", buf, strerror(ret)); + return ret; + } ); } /* Create the new object */ @@ -245,14 +257,19 @@ tmp->is_local = 1; } else { /* Copy the fqdn */ - CHECK_MALLOC( tmp->fqdn = strdup(buf) ); - tmp->fqdn_len = strlen(tmp->fqdn); + tmp->fqdn = fqdn; + tmp->fqdn_len = fqdn_len; + /* Find an appropriate realm */ - tmp->realm = strchr(tmp->fqdn, '.'); - if (tmp->realm) + tmp->realm = strchr(fqdn, '.'); + if (tmp->realm) { tmp->realm += 1; - if ((!tmp->realm) || (*tmp->realm == '\0')) /* in case the fqdn was "localhost." for example, if it is possible... */ + tmp->realm_len = tmp->fqdn_len - (tmp->realm - fqdn); + } + if ((!tmp->realm) || (*tmp->realm == '\0')) { /* in case the fqdn was "localhost." for example, if it is possible... */ tmp->realm = fd_g_config->cnf_diamrlm; + tmp->realm_len = fd_g_config->cnf_diamrlm_len; + } } /* move the sa info reference */ @@ -281,7 +298,7 @@ /* Free the data */ for (idx = 0; idx < client->aliases_nb; idx++) - free(client->aliases[idx]); + free(client->aliases[idx].name); free(client->aliases); free(client->fqdn); free(client->sa); @@ -531,9 +548,10 @@ int idx; int valid_nas_info = 0; struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL; - char * oh_str = NULL; - char * or_str = NULL; - char * rr_str = NULL; + size_t nas_id_len; + char * oh_str = NULL; size_t oh_strlen; int oh_free = 0; + char * or_str = NULL; size_t or_strlen; + char * rr_str = NULL; size_t rr_strlen; char buf[REVERSE_DNS_SIZE_MAX]; /* to store DNS lookups results */ struct avp *avp = NULL; @@ -554,6 +572,7 @@ if ((attr->type == RADIUS_ATTR_NAS_IDENTIFIER) && (attr_len > 0)) { nas_id = attr; + nas_id_len = attr_len; continue; } @@ -567,7 +586,7 @@ TRACE_DEBUG(FULL, "The message does not contain any NAS identification attribute."); /* Get information on this peer */ - CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) ); goto diameter; } @@ -618,12 +637,14 @@ TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different \nfrom the sender's. Please configure as Proxy if this is expected.\n Message discarded."); return EINVAL; } else { + int ret; + sSS ss; /* the peer is configured as a proxy, or running on localhost, so accept the message */ - sSS ss; /* In that case, the cli will be stored as Route-Record and the NAS-IP-Address as origin */ if (!cli->is_local) { rr_str = cli->fqdn; + rr_strlen = cli->fqdn_len; } /* We must DNS-reverse the NAS-IP*-Address */ @@ -640,25 +661,39 @@ CHECK_SYS_DO( getnameinfo( (sSA *)&ss, sSAlen(&ss), &buf[0], sizeof(buf), NULL, 0, NI_NAMEREQD), { if (cli->is_local) { - CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) ); goto diameter; } TRACE_DEBUG(INFO, "The NAS-IP*-Address cannot be DNS reversed in order to create the Origin-Host AVP; rejecting the message (translation is impossible)."); return EINVAL; } ); + + oh_str = &buf[0]; + CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1), + { + if (cli->is_local) { + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) ); + goto diameter; + } + + TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret)); + return ret; + } ); + oh_free = 1; - oh_str = &buf[0]; or_str = strchr(oh_str, '.'); if (or_str) { or_str ++; /* move after the first dot */ if (*or_str == '\0') or_str = NULL; /* Discard this realm, we will use the local realm later */ + else + or_strlen = oh_strlen - (or_str - oh_str); } } } else { /* The attribute matches the source address, just use this in origin-host */ - CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) ); } goto diameter; /* we ignore the nas_id in that case */ @@ -667,7 +702,7 @@ /* We don't have a NAS-IP*-Address attribute if we are here */ if (cli->is_local) { /* Simple: we use our own configuration */ - CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) ); goto diameter; } @@ -696,14 +731,14 @@ */ /* first, check if the nas_id is the fqdn of the peer or a known alias */ - if ((cli->fqdn_len == (nas_id->length - sizeof(struct radius_attr_hdr))) - && (!strncasecmp((char *)(nas_id + 1), cli->fqdn, nas_id->length - sizeof(struct radius_attr_hdr)))) { + if (!fd_os_almostcasecmp(nas_id + 1, nas_id_len, + cli->fqdn, cli->fqdn_len)) { TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the client"); found = 1; } else { for (idx = 0; idx < cli->aliases_nb; idx++) { - if (((nas_id->length - sizeof(struct radius_attr_hdr)) == strlen(cli->aliases[idx])) - && (!strncasecmp((char *)(nas_id + 1), cli->aliases[idx], nas_id->length - sizeof(struct radius_attr_hdr)))) { + if (!fd_os_cmp(nas_id + 1, nas_id_len, + cli->aliases[idx].name, cli->aliases[idx].len)) { TRACE_DEBUG(FULL, "NAS-Identifier valid value found in the cache"); found = 1; break; @@ -713,14 +748,14 @@ if (found) { /* The NAS-Identifier matches the source IP */ - CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) ); goto diameter; } /* Attempt DNS resolution of the identifier */ - ASSERT( nas_id->length - sizeof(struct radius_attr_hdr) < sizeof(buf) ); - memcpy(buf, nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr)); + ASSERT( nas_id_len < sizeof(buf) ); + memcpy(buf, nas_id + 1, nas_id_len); buf[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0'; /* Now check if this alias is valid for this peer */ @@ -744,44 +779,56 @@ if (!found) { if (cli->type == RGW_CLI_NAS) { TRACE_DEBUG(INFO, "The NAS-Identifier value '%.*s' resolves to a different IP than the client's, discarding the message. \nConfigure this client as a Proxy if this message should be valid.", - nas_id->length - sizeof(struct radius_attr_hdr), nas_id + 1); + nas_id_len, nas_id + 1); return EINVAL; } else { /* This identifier matches a different IP, assume it is a proxied message */ if (!cli->is_local) { rr_str = cli->fqdn; + rr_strlen = cli->fqdn_len; } oh_str = &buf[0]; /* The canonname resolved */ + CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1), + { + TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret)); + return ret; + } ); + oh_free = 1; or_str = strchr(oh_str, '.'); if (or_str) { or_str ++; /* move after the first dot */ if (*or_str == '\0') or_str = NULL; /* Discard this realm, we will use the local realm later */ + else + or_strlen = oh_strlen - (or_str - oh_str); } } } else { /* It is a valid alias, save it */ - CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(char *)) ); - CHECK_MALLOC( cli->aliases[cli->aliases_nb + 1] = malloc( 1 + nas_id->length - sizeof(struct radius_attr_hdr) )); - memcpy( cli->aliases[cli->aliases_nb + 1], nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr)); - *(cli->aliases[cli->aliases_nb + 1] + nas_id->length - sizeof(struct radius_attr_hdr)) = '\0'; + CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(cli->aliases[0])) ); + + CHECK_MALLOC( cli->aliases[cli->aliases_nb + 1].name = os0dup(nas_id + 1, nas_id_len ) ); + cli->aliases[cli->aliases_nb + 1].len = nas_id_len; + cli->aliases_nb ++; - TRACE_DEBUG(FULL, "Saved valid alias for client: '%s' -> '%s'", cli->aliases[cli->aliases_nb + 1], cli->fqdn); - CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + TRACE_DEBUG(FULL, "Saved valid alias for client: '%.*s' -> '%s'", nas_id_len, nas_id + 1, cli->fqdn); + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) ); } } else { /* Error resolving the name */ TRACE_DEBUG(INFO, "NAS-Identifier '%s' cannot be resolved: %s. Ignoring...", buf, gai_strerror(ret)); /* Assume this is a valid identifier for the client */ - CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) ); + CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) ); } } /* Now, let's create the empty Diameter message with Origin-Host, -Realm, and Route-Record if needed. */ diameter: ASSERT(oh_str); /* If it is not defined here, there is a bug... */ - if (!or_str) + if (!or_str) { or_str = fd_g_config->cnf_diamrlm; /* Use local realm in that case */ + or_strlen = fd_g_config->cnf_diamrlm_len; + } /* Create an empty Diameter message so that extensions can store their AVPs */ CHECK_FCT( fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam ) ); @@ -790,7 +837,7 @@ CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) ); memset(&avp_val, 0, sizeof(avp_val)); avp_val.os.data = (unsigned char *)oh_str; - avp_val.os.len = strlen(oh_str); + avp_val.os.len = oh_strlen; CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); @@ -798,7 +845,7 @@ CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) ); memset(&avp_val, 0, sizeof(avp_val)); avp_val.os.data = (unsigned char *)or_str; - avp_val.os.len = strlen(or_str); + avp_val.os.len = or_strlen; CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); @@ -806,28 +853,37 @@ CHECK_FCT( fd_msg_avp_new ( cache_route_record, 0, &avp ) ); memset(&avp_val, 0, sizeof(avp_val)); avp_val.os.data = (unsigned char *)rr_str; - avp_val.os.len = strlen(rr_str); + avp_val.os.len = rr_strlen; CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); } + if (oh_free) + free(oh_str); + /* Done! */ return 0; } -int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm) +int rgw_clients_get_origin(struct rgw_client *cli, DiamId_t *fqdn, size_t *fqdnlen, DiamId_t *realm, size_t *realmlen) { - TRACE_ENTRY("%p %p %p", cli, fqdn, realm); - CHECK_PARAMS(cli && fqdn); + TRACE_ENTRY("%p %p %p %p %p", cli, fqdn, fqdnlen, realm, realmlen); + CHECK_PARAMS(cli && fqdn && fqdnlen); if (cli->is_local) { *fqdn = fd_g_config->cnf_diamid; + *fqdnlen = fd_g_config->cnf_diamid_len; if (realm) *realm= fd_g_config->cnf_diamrlm; + if (realmlen) + *realmlen= fd_g_config->cnf_diamrlm_len; } else { *fqdn = cli->fqdn; + *fqdnlen = cli->fqdn_len; if (realm) *realm= cli->realm; + if (realmlen) + *realmlen= cli->realm_len; } return 0;
--- a/extensions/app_radgw/rgw_common.h Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/app_radgw/rgw_common.h Wed Feb 09 15:26:58 2011 +0900 @@ -58,7 +58,7 @@ int rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len); char * rgw_clients_id(struct rgw_client *cli); -int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm); +int rgw_clients_get_origin(struct rgw_client *cli, DiamId_t *fqdn, size_t *fqdnlen, DiamId_t *realm, size_t *realmlen); /* Each plugin must provide the following structure. */ extern struct rgw_api {
--- a/extensions/app_radgw/rgw_servers.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/app_radgw/rgw_servers.c Wed Feb 09 15:26:58 2011 +0900 @@ -147,8 +147,9 @@ /* parse the message, loop if message is invalid */ CHECK_FCT_DO( rgw_msg_parse(&buf[0], len, &msg), { - char * cliname = NULL; - CHECK_FCT_DO( rgw_clients_get_origin(nas_info, &cliname, NULL), ); + DiamId_t cliname = NULL; + size_t clisz; + CHECK_FCT_DO( rgw_clients_get_origin(nas_info, &cliname, &clisz, NULL, NULL), ); TRACE_DEBUG(INFO, "Discarding invalid RADIUS message from '%s'", cliname); rgw_clients_dispose(&nas_info); continue;
--- a/extensions/app_radgw/rgwx_acct.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/app_radgw/rgwx_acct.c Wed Feb 09 15:26:58 2011 +0900 @@ -305,9 +305,9 @@ const char * prefix = "Diameter/"; size_t pref_len; - uint8_t * si = NULL; + os0_t si = NULL; size_t si_len = 0; - uint8_t * un = NULL; + os0_t un = NULL; size_t un_len = 0; TRACE_ENTRY("%p %p %p %p %p %p", cs, session, rad_req, rad_ans, diam_fw, cli); @@ -486,7 +486,7 @@ /* Create the Session-Id AVP if needed */ if (!*session) { - CHECK_FCT( fd_sess_fromsid ( (char *)/* cast should be removed later */si, si_len, session, NULL) ); + CHECK_FCT( fd_sess_fromsid ( si, si_len, session, NULL) ); TRACE_DEBUG(FULL, "[acct.rgwx] Translating new accounting message for session '%.*s'...", si_len, si); @@ -662,13 +662,13 @@ char buf[32]; char * attr_val, *auth_val; attr_val = (char *)(attr + 1); - auth_val = attr_val + strlen(CLASS_AAI_PREFIX); - if ( (attr->length > sizeof(struct radius_attr_hdr) + strlen(CLASS_AAI_PREFIX) ) - && (attr->length < sizeof(struct radius_attr_hdr) + strlen(CLASS_AAI_PREFIX) + sizeof(buf)) - && ! strncmp(attr_val, CLASS_AAI_PREFIX, strlen(CLASS_AAI_PREFIX))) { + auth_val = attr_val + CONSTSTRLEN(CLASS_AAI_PREFIX); + if ( (attr->length > sizeof(struct radius_attr_hdr) + CONSTSTRLEN(CLASS_AAI_PREFIX) ) + && (attr->length < sizeof(struct radius_attr_hdr) + CONSTSTRLEN(CLASS_AAI_PREFIX) + sizeof(buf)) + && ! strncmp(attr_val, CLASS_AAI_PREFIX, CONSTSTRLEN(CLASS_AAI_PREFIX))) { memset(buf, 0, sizeof(buf)); - memcpy(buf, auth_val, attr->length - sizeof(struct radius_attr_hdr) - strlen(CLASS_AAI_PREFIX)); + memcpy(buf, auth_val, attr->length - sizeof(struct radius_attr_hdr) - CONSTSTRLEN(CLASS_AAI_PREFIX)); if (sscanf(buf, "%u", &auth_appl) == 1) { TRACE_DEBUG(ANNOYING, "Found Class attribute with '%s' prefix (attr #%d), AAI:%u.", CLASS_AAI_PREFIX, idx, auth_appl); } @@ -1300,8 +1300,10 @@ if (st->send_str) { struct msg * str = NULL; struct msg_hdr * hdr = NULL; - char * fqdn; - char * realm; + DiamId_t fqdn; + size_t fqdn_len; + DiamId_t realm; + size_t realm_len; union avp_value avp_val; /* Create a new STR message */ @@ -1322,13 +1324,13 @@ CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) ); /* Get information on the NAS */ - CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) ); + CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdn_len, &realm, &realm_len) ); /* Add the Origin-Host as next AVP */ CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_Host, 0, &avp ) ); memset(&avp_val, 0, sizeof(avp_val)); avp_val.os.data = (unsigned char *)fqdn; - avp_val.os.len = strlen(fqdn); + avp_val.os.len = fqdn_len; CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) ); @@ -1336,7 +1338,7 @@ CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_Realm, 0, &avp ) ); memset(&avp_val, 0, sizeof(avp_val)); avp_val.os.data = (unsigned char *)realm; - avp_val.os.len = strlen(realm); + avp_val.os.len = realm_len; CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
--- a/extensions/app_radgw/rgwx_auth.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/app_radgw/rgwx_auth.c Wed Feb 09 15:26:58 2011 +0900 @@ -238,13 +238,13 @@ int got_empty_eap = 0; const char * prefix = "Diameter/"; size_t pref_len; - uint8_t * dh = NULL; + os0_t dh = NULL; size_t dh_len = 0; - uint8_t * dr = NULL; + os0_t dr = NULL; size_t dr_len = 0; - uint8_t * si = NULL; + os0_t si = NULL; size_t si_len = 0; - uint8_t * un = NULL; + os0_t un = NULL; size_t un_len = 0; size_t nattr_used = 0; struct avp ** avp_tun = NULL, *avp = NULL; @@ -292,7 +292,7 @@ to the NAS-IP-Address attribute (preferred if available), and/or to the NAS-Identifier attribute. (Note that the RADIUS NAS-Identifier is not required to be an FQDN.) - -> done in rgw_msg_create_base. + -> done in rgw_clients_create_origin. - The response MUST have an Origin-AAA-Protocol AVP added, indicating the protocol of origin of the message. @@ -452,27 +452,30 @@ /* Create the session if it is not already done */ if (*session == NULL) { - char * sess_str = NULL; + os0_t sess_str = NULL; + size_t sess_strlen; if (si_len) { /* We already have the Session-Id, just use it */ - CHECK_FCT( fd_sess_fromsid ( (char *) /* this cast will be removed later */ si, si_len, session, NULL) ); + CHECK_FCT( fd_sess_fromsid ( si, si_len, session, NULL) ); } else { /* Create a new Session-Id string */ - char * fqdn; - char * realm; + DiamId_t fqdn; + size_t fqdnlen; + DiamId_t realm; + size_t realmlen; /* Get information on the RADIUS client */ - CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) ); + CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdnlen, &realm, &realmlen) ); /* If we have a user name, create the new session with it */ if (un) { int len; - /* If not found, create a new Session-Id. The format is: {fqdn;hi32;lo32;username;diamid} */ + /* If not found, create a new Session-Id. Our format is: {fqdn;hi32;lo32;username;diamid} */ CHECK_MALLOC( sess_str = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) ); - len = sprintf(sess_str, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid); - CHECK_FCT( fd_sess_new(session, fqdn, sess_str, len) ); + len = sprintf((char *)sess_str, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid); + CHECK_FCT( fd_sess_new(session, fqdn, fqdnlen, sess_str, len) ); free(sess_str); } else { /* We don't have enough information to create the Session-Id, the RADIUS message is probably invalid */ @@ -482,14 +485,14 @@ } /* Now, add the Session-Id AVP at beginning of Diameter message */ - CHECK_FCT( fd_sess_getsid(*session, &sess_str) ); + CHECK_FCT( fd_sess_getsid(*session, &sess_str, &sess_strlen) ); TRACE_DEBUG(FULL, "[auth.rgwx] Translating new message for session '%s'...", sess_str); /* Add the Session-Id AVP as first AVP */ CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) ); - value.os.data = (unsigned char *)sess_str; - value.os.len = strlen(sess_str); + value.os.data = sess_str; + value.os.len = sess_strlen; CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) ); } @@ -563,18 +566,18 @@ /* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */ #define CONV2DIAM_STR( _dictobj_ ) \ - CHECK_PARAMS( attr->length >= 2 ); \ + CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) ); \ /* Create the AVP with the specified dictionary model */ \ CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \ - value.os.len = attr->length - 2; \ - value.os.data = (unsigned char *)(attr + 1); \ + value.os.len = attr->length - sizeof(struct radius_attr_hdr); \ + value.os.data = (os0_t)(attr + 1); \ CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \ /* Add the AVP in the Diameter message. */ \ CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \ /* Same thing, for scalar AVPs of 32 bits */ #define CONV2DIAM_32B( _dictobj_ ) \ - CHECK_PARAMS( attr->length == 6 ); \ + CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint32_t) );\ CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \ { \ uint8_t * v = (uint8_t *)(attr + 1); \ @@ -588,7 +591,7 @@ /* And the 64b version */ #define CONV2DIAM_64B( _dictobj_ ) \ - CHECK_PARAMS( attr->length == 10); \ + CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint64_t) );\ CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \ { \ uint8_t * v = (uint8_t *)(attr + 1); \ @@ -608,7 +611,7 @@ /* - The Destination-Realm AVP is created from the information found in the RADIUS User-Name attribute. - -> done in rgw_msg_create_base + -> done in rgw_clients_create_origin */ case RADIUS_ATTR_USER_NAME: CONV2DIAM_STR( User_Name );
--- a/extensions/app_radgw/rgwx_debug.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/app_radgw/rgwx_debug.c Wed Feb 09 15:26:58 2011 +0900 @@ -101,8 +101,9 @@ if (!session || ! *session) { fd_log_debug(" Diameter session: NULL pointer\n"); } else { - char * str; - CHECK_FCT( fd_sess_getsid(*session, &str) ); + os0_t str; + size_t str_len; + CHECK_FCT( fd_sess_getsid(*session, &str, &str_len) ); fd_log_debug(" Diameter session: %s\n", str); }
--- a/extensions/app_radgw/rgwx_sip.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/app_radgw/rgwx_sip.c Wed Feb 09 15:26:58 2011 +0900 @@ -57,38 +57,38 @@ /* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */ #define CONV2DIAM_STR( _dictobj_ ) \ - CHECK_PARAMS( attr->length >= 2 ); \ + CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) ); \ /* Create the AVP with the specified dictionary model */ \ CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \ - value.os.len = attr->length - 2; \ - value.os.data = (unsigned char *)(attr + 1); \ + value.os.len = attr->length - sizeof(struct radius_attr_hdr); \ + value.os.data = (os0_t)(attr + 1); \ CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \ /* Add the AVP in the Diameter message. */ \ CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \ #define CONV2DIAM_STR_AUTH( _dictobj_ ) \ - CHECK_PARAMS( attr->length >= 2 ); \ + CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) ); \ /* Create the AVP with the specified dictionary model */ \ CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \ - value.os.len = attr->length - 2; \ - value.os.data = (unsigned char *)(attr + 1); \ + value.os.len = attr->length - sizeof(struct radius_attr_hdr); \ + value.os.data = (os0_t)(attr + 1); \ CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \ /* Add the AVP in the Diameter message. */ \ - CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) ); \ + CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) ); \ /* Same thing, for scalar AVPs of 32 bits */ #define CONV2DIAM_32B( _dictobj_ ) \ - CHECK_PARAMS( attr->length == 6 ); \ + CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint32_t) );\ CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \ { \ uint8_t * v = (uint8_t *)(attr + 1); \ value.u32 = (v[0] << 24) \ - | (v[1] << 16) \ - | (v[2] << 8) \ - | v[3] ; \ + | (v[1] << 16) \ + | (v[2] << 8) \ + | v[3] ; \ } \ CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \ - CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \ @@ -144,26 +144,24 @@ struct noncechain { struct fd_list chain; - char * sid; + os0_t sid; size_t sidlen; - char * nonce; + os0_t nonce; size_t noncelen; }; -static int nonce_add_element(char * nonce, size_t noncelen,char * sid, size_t sidlen, struct rgwp_config * state) +static int nonce_add_element(os0_t nonce, size_t noncelen, os0_t sid, size_t sidlen, struct rgwp_config * state) { CHECK_PARAMS(nonce && state && sid && sidlen && noncelen); noncechain *newelt; CHECK_MALLOC(newelt=malloc(sizeof(noncechain))); - CHECK_MALLOC(newelt->nonce=malloc(noncelen)); - memcpy(newelt->nonce,nonce,noncelen); + CHECK_MALLOC(newelt->nonce= os0dup(nonce, noncelen)); newelt->noncelen=noncelen; - CHECK_MALLOC(newelt->sid=malloc(sidlen)); - memcpy(newelt->sid,sid,sidlen); + CHECK_MALLOC(newelt->sid=os0dup(sid, sidlen)); newelt->sidlen=sidlen; fd_list_init(&newelt->chain,NULL); @@ -197,21 +195,21 @@ } */ //Retrieve sid from nonce -static char * nonce_get_sid(char * nonce, size_t noncelen, size_t * sidlen, struct rgwp_config *state) +static os0_t nonce_get_sid(os0_t nonce, size_t noncelen, size_t * sidlen, struct rgwp_config *state) { struct fd_list * li; - char *sid=NULL; + os0_t sid=NULL; CHECK_PARAMS_DO(nonce && state && noncelen && sidlen, return NULL); *sidlen=0; - //**Start mutex + // **Start mutex CHECK_POSIX_DO(pthread_mutex_lock(&state->nonce_mutex),); for(li=state->listnonce.next;li!=&state->listnonce;li=li->next) { noncechain *temp=(noncechain *)li; - if(temp->noncelen==noncelen && strncmp(temp->nonce,nonce, noncelen)==0) + if (!fd_os_cmp(temp->nonce, temp->noncelen, nonce, noncelen)) { fd_list_unlink (li); sid=temp->sid; @@ -223,13 +221,13 @@ } CHECK_POSIX_DO(pthread_mutex_unlock(&state->nonce_mutex),); - //***Stop mutex + // ***Stop mutex return sid; } static void nonce_deletelistnonce(struct rgwp_config *state) { - //**Start mutex + // **Start mutex CHECK_POSIX_DO(pthread_mutex_lock(&state->nonce_mutex),); while(!(FD_IS_LIST_EMPTY(&state->listnonce)) ) { @@ -242,7 +240,7 @@ } CHECK_POSIX_DO(pthread_mutex_unlock(&state->nonce_mutex),); - //***Stop mutex + // ***Stop mutex } /* Initialize the plugin */ @@ -337,8 +335,9 @@ int got_Dcnonce = 0; int got_Dresponse = 0; int got_Dalgorithm = 0; - char * sid = NULL; - char * un=NULL; + os0_t sid = NULL; + size_t sidlen; + os0_t un=NULL; size_t un_len; size_t nattr_used = 0; struct avp *auth_data=NULL, *auth=NULL, *avp = NULL; @@ -351,7 +350,7 @@ //We check that session is not already filled if(*session) { - TRACE_DEBUG(INFO,"We are not supposed to receive a session in radSIP plugin."); + TRACE_DEBUG(INFO,"INTERNAL ERROR: We are not supposed to receive a session in radSIP plugin."); return EINVAL; } @@ -370,7 +369,7 @@ if (attr->length>sizeof(struct radius_attr_hdr)) { TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", attr->length- sizeof(struct radius_attr_hdr), (char *)(attr+1)); - un = (char *)(attr + 1); + un = (os0_t)(attr + 1); un_len =attr->length - sizeof(struct radius_attr_hdr); } break; @@ -395,9 +394,7 @@ case RADIUS_ATTR_DIGEST_NONCE: got_Dnonce = 1; - size_t sidlen; - - sid=nonce_get_sid((char *)(attr+1),attr->length-2,&sidlen,cs); + sid=nonce_get_sid((os0_t)(attr+1), attr->length - sizeof(struct radius_attr_hdr), &sidlen, cs); if(!sid) { TRACE_DEBUG(INFO,"We haven't found the session.'"); @@ -405,7 +402,7 @@ } CHECK_FCT(fd_sess_fromsid (sid, sidlen, session, NULL)); free(sid); - + break; case RADIUS_ATTR_DIGEST_CNONCE: @@ -431,20 +428,21 @@ /* Create the session if it is not already done */ if (!*session) { - char * fqdn; - char * realm; + DiamId_t fqdn; + size_t fqdn_len; + DiamId_t realm; + size_t realm_len; /* Get information on the RADIUS client */ - CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) ); + CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdn_len, &realm, &realm_len) ); - int len; /* Create a new Session-Id. The format is: {fqdn;hi32;lo32;username;diamid} */ CHECK_MALLOC( sid = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) ); - len = sprintf(sid, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid); - CHECK_FCT( fd_sess_new(session, fqdn, sid, len) ); + sidlen = sprintf((char *)sid, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid); + CHECK_FCT( fd_sess_new(session, fqdn, fqdn_len, sid, sidlen) ); free(sid); } @@ -452,21 +450,21 @@ CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) ); int i = 0; - if (un) { - /* Is there an '@' in the user name? We don't care for decorated NAI here */ - for (i = un_len - 2; i > 0; i--) { - if (un[i] == '@') { - i++; - break; - } + + /* Is there an '@' in the user name? We don't care for decorated NAI here */ + for (i = un_len - 2; i > 0; i--) { + if (un[i] == '@') { + i++; + break; } } + if (i == 0) { /* Not found in the User-Name => we use the local domain of this gateway */ - value.os.data = (unsigned char *)fd_g_config->cnf_diamrlm; + value.os.data = (os0_t)fd_g_config->cnf_diamrlm; value.os.len = fd_g_config->cnf_diamrlm_len; } else { - value.os.data = (unsigned char *)(un + i); + value.os.data = un + i; value.os.len = un_len - i; } @@ -474,14 +472,14 @@ CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) ); /* Now, add the Session-Id AVP at beginning of Diameter message */ - CHECK_FCT( fd_sess_getsid(*session, &sid) ); + CHECK_FCT( fd_sess_getsid(*session, &sid, &sidlen) ); TRACE_DEBUG(FULL, "[sip.rgwx] Translating new message for session '%s'...", sid); /* Add the Session-Id AVP as first AVP */ CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) ); - value.os.data = (unsigned char *)sid; - value.os.len = strlen(sid); + value.os.data = sid; + value.os.len = sidlen; CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) ); @@ -551,7 +549,6 @@ CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Authorization, 0, &auth ) ); CHECK_FCT( fd_msg_avp_add ( auth_data, MSG_BRW_LAST_CHILD, auth) ); } - char * temp=NULL,*sipuri=NULL; for (idx = 0; idx < rad_req->attr_used; idx++) { @@ -596,31 +593,37 @@ if(!got_Drealm) { //We extract Realm from Digest_URI - char *realm=NULL; + DiamId_t realm=NULL; + size_t realm_len; + os0_t temp; + + temp = (os0_t)(attr + 1); + + for (i=attr->length - sizeof(struct radius_attr_hdr) - 1; i>=0; i--) { + if (temp[i] == '@') { + realm = (DiamId_t)temp + i + 1; + CHECK_FCT_DO( fd_os_validate_DiameterIdentity(&realm, &realm_len, 1), + realm = NULL ); + break; + } + } - CHECK_MALLOC(temp=malloc(attr->length -1)); - strncpy(temp, (char *)(attr + 1), attr->length -2); - temp[attr->length-2] = '\0'; - - realm = strtok( (char *)(temp), "@" ); - realm = strtok( NULL, "@" ); - free(temp); - temp=NULL; if(realm!=NULL) { CHECK_FCT( fd_msg_avp_new ( cs->dict.Digest_Realm, 0, &avp ) ); - value.os.data=(unsigned char *)realm; - value.os.len=strlen(realm); + value.os.data=(os0_t)realm; + value.os.len=realm_len; CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) ); //We add SIP-Server-URI AVP because SIP server is registrar (through gateway) CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Server_URI, 0, &avp ) ); - value.os.data=(unsigned char *)realm; - value.os.len=strlen(realm); + value.os.data=(os0_t)realm; + value.os.len=realm_len; CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); + free(realm); } else { @@ -640,20 +643,17 @@ //We add SIP-Server-URI AVP because SIP server is registrar (through gateway) CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Server_URI, 0, &avp ) ); - - - CHECK_MALLOC(temp=malloc(attr->length -1)); - strncpy(temp, (char *)(attr + 1), attr->length -2); - + os0_t temp; + #define SIP_PREFIX "sip:" + size_t temp_len = attr->length - sizeof(struct radius_attr_hdr) + CONSTSTRLEN(SIP_PREFIX) + 1; + CHECK_MALLOC( temp = malloc(temp_len) ); + temp_len = snprintf((char *)temp, temp_len, SIP_PREFIX "%.*s", attr->length - sizeof(struct radius_attr_hdr), (char *)(attr + 1)); - CHECK_MALLOC(sipuri=malloc(attr->length +3)); - strcpy(sipuri,"sip:"); - strcat(sipuri,(const char *)temp); - value.os.data=(unsigned char *)sipuri; - value.os.len=attr->length +2; + value.os.data=temp; + value.os.len=temp_len; free(temp); - temp=NULL; + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); break; @@ -688,11 +688,12 @@ if(!got_Dalgorithm) { //[Note 3] If Digest-Algorithm is missing, 'MD5' is assumed. + #define DIGEST_ALGO_MD5 "MD5" CHECK_FCT( fd_msg_avp_new ( cs->dict.Digest_Algorithm, 0, &avp ) ); - value.os.data = (unsigned char *)"MD5"; - value.os.len = strlen((const char *)value.os.data); + value.os.data = (os0_t)DIGEST_ALGO_MD5; + value.os.len = CONSTSTRLEN(DIGEST_ALGO_MD5) - 1; CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) ); got_Dalgorithm=1; @@ -830,12 +831,11 @@ CONV2RAD_STR(DIAM_ATTR_DIGEST_NONCE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0); /* Retrieve the request identified which was stored in the session */ if (session) { - char *sid=NULL; + os0_t sid=NULL; size_t sidlen; - fd_sess_getsid (session, &sid ); - sidlen=strlen(sid); + fd_sess_getsid (session, &sid, &sidlen ); - nonce_add_element((char *)ahdr->avp_value->os.data,ahdr->avp_value->os.len, sid,sidlen, cs); + nonce_add_element(ahdr->avp_value->os.data, ahdr->avp_value->os.len, sid, sidlen, cs); } break; case DIAM_ATTR_DIGEST_REALM:
--- a/extensions/app_sip/registrationtermination.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/app_sip/registrationtermination.c Wed Feb 09 15:26:58 2011 +0900 @@ -146,12 +146,14 @@ // Create a new session { - CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, "app_sip", 7 )); - char * sid; - CHECK_FCT( fd_sess_getsid ( sess, &sid )); + #define APP_SIP_SID_OPT "app_sip" + CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)APP_SIP_SID_OPT, CONSTSTRLEN(APP_SIP_SID_OPT) )); + os0_t sid; + size_t sidlen; + CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen )); CHECK_FCT( fd_msg_avp_new ( sip_dict.Session_Id, 0, &avp )); - value.os.data = (uint8_t *)sid; - value.os.len = strlen(sid); + value.os.data = sid; + value.os.len = sidlen; CHECK_FCT( fd_msg_avp_setvalue( avp, &value )); CHECK_FCT( fd_msg_avp_add( message, MSG_BRW_FIRST_CHILD, avp )); }
--- a/extensions/dbg_interactive/dbg_interactive.i Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/dbg_interactive/dbg_interactive.i Wed Feb 09 15:26:58 2011 +0900 @@ -151,7 +151,7 @@ %} /* Overwrite declaration to apply typemaps */ -int fd_sess_fromsid ( char * STRING, size_t LENGTH, struct session ** OUTPUT, int * BOOL_OUT); +int fd_sess_fromsid ( unsigned char * STRING, size_t LENGTH, struct session ** OUTPUT, int * BOOL_OUT); /*********************************************************
--- a/extensions/dbg_interactive/messages.i Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/dbg_interactive/messages.i Wed Feb 09 15:26:58 2011 +0900 @@ -340,14 +340,14 @@ } /* Get the source */ - char *source() { - char * s = NULL; - int ret = fd_msg_source_get($self, &s); + %cstring_output_allocate_size(char ** outid, size_t * outlen, /* do not free */); + void source(char ** outid, size_t * outlen) { + int ret = fd_msg_source_get($self, outid, outlen); if (ret != 0) { DI_ERROR(ret, NULL, NULL); - return NULL; + return; } - return s; + return; } /* Get the session */
--- a/extensions/dbg_interactive/peers.i Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/dbg_interactive/peers.i Wed Feb 09 15:26:58 2011 +0900 @@ -96,9 +96,9 @@ } %inline %{ -static struct peer_hdr * peer_search(char *diamid) { +static struct peer_hdr * peer_search(char *STRING, size_t LENGTH) { struct peer_hdr *r = NULL; - int ret = fd_peer_getbyid( diamid, &r ); + int ret = fd_peer_getbyid( STRING, LENGTH, 0, &r ); if (ret) { DI_ERROR(ret, NULL, NULL); return NULL;
--- a/extensions/dbg_interactive/routing.i Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/dbg_interactive/routing.i Wed Feb 09 15:26:58 2011 +0900 @@ -54,17 +54,19 @@ struct rt_data *r = self; fd_rtd_free(&r); } - void add(char * peerid, char * realm) { - int ret = fd_rtd_candidate_add($self, peerid, realm); + %apply (char *STRING, int LENGTH) { (char * peerid, size_t peeridlen) }; + %apply (char *STRING, int LENGTH) { (char * realm, size_t realmlen) }; + void add(char * peerid, size_t peeridlen, char * realm, size_t realmlen) { + int ret = fd_rtd_candidate_add($self, peerid, peeridlen, realm, realmlen); if (ret != 0) { DI_ERROR(ret, NULL, NULL); } } void remove(char * STRING, size_t LENGTH) { - fd_rtd_candidate_del($self, STRING, LENGTH); + fd_rtd_candidate_del($self, (os0_t)STRING, LENGTH); } - void error(char * dest, char * STRING, size_t LENGTH, uint32_t rcode) { - int ret = fd_rtd_error_add($self, dest, (uint8_t *)STRING, LENGTH, rcode); + void error(char * peerid, size_t peeridlen, char * STRING, size_t LENGTH, uint32_t rcode) { + int ret = fd_rtd_error_add($self, peerid, peeridlen, (os0_t)STRING, LENGTH, rcode); if (ret != 0) { DI_ERROR(ret, NULL, NULL); }
--- a/extensions/dbg_interactive/sessions.i Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/dbg_interactive/sessions.i Wed Feb 09 15:26:58 2011 +0900 @@ -39,7 +39,7 @@ %{ /* call it (might be called from a different thread than the interpreter, when session times out) */ -static void call_the_python_cleanup_callback(session_state * state, char * sid, void * cb) { +static void call_the_python_cleanup_callback(session_state * state, os0_t sid, void * cb) { PyObject *result; if (!cb) { fd_log_debug("Internal error: missing callback object!\n"); @@ -99,7 +99,7 @@ session() { int ret; struct session * s = NULL; - ret = fd_sess_new(&s, fd_g_config->cnf_diamid, "dbg_interactive", sizeof("dbg_interactive")); + ret = fd_sess_new(&s, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)"dbg_interactive", CONSTSTRLEN("dbg_interactive")); if (ret != 0) { DI_ERROR(ret, NULL, NULL); return NULL; @@ -109,7 +109,7 @@ session(char * diamid, char * STRING, size_t LENGTH) { int ret; struct session * s = NULL; - ret = fd_sess_new(&s, diamid, STRING, LENGTH); + ret = fd_sess_new(&s, diamid, 0, (os0_t)STRING, LENGTH); if (ret != 0) { DI_ERROR(ret, NULL, NULL); return NULL; @@ -119,7 +119,7 @@ session(char * STRING, size_t LENGTH) { int ret, n; struct session * s = NULL; - ret = fd_sess_fromsid(STRING, LENGTH, &s, &n); + ret = fd_sess_fromsid((os0_t)STRING, LENGTH, &s, &n); if (ret != 0) { DI_ERROR(ret, NULL, NULL); return NULL; @@ -143,15 +143,16 @@ } return; } - char * getsid() { + + %cstring_output_allocate_size(char ** outsid, size_t * sidlen, /* do not free */); + void getsid(char ** outsid, size_t * sidlen) { int ret; - char * sid = NULL; - ret = fd_sess_getsid( $self, &sid); + ret = fd_sess_getsid( $self, (void *)outsid, sidlen); if (ret != 0) { DI_ERROR(ret, NULL, NULL); - return NULL; + return; } - return sid; + return; } void settimeout(long seconds) { struct timespec timeout;
--- a/extensions/test_app/ta_bench.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/test_app/ta_bench.c Wed Feb 09 15:26:58 2011 +0900 @@ -129,7 +129,8 @@ CHECK_FCT_DO( fd_msg_new( ta_cmd_r, MSGFL_ALLOC_ETEID, &req ), goto out ); /* Create a new session */ - CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, "app_test", 8 ), goto out ); + #define TEST_APP_SID_OPT "app_testb" + CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)TEST_APP_SID_OPT, CONSTSTRLEN(TEST_APP_SID_OPT) ), goto out ); /* Create the random value to store with the session */ mi = malloc(sizeof(struct ta_mess_info)); @@ -144,11 +145,12 @@ /* Session-Id */ { - char * sid; - CHECK_FCT_DO( fd_sess_getsid ( sess, &sid ), goto out ); + os0_t sid; + size_t sidlen; + CHECK_FCT_DO( fd_sess_getsid ( sess, &sid, &sidlen ), goto out ); CHECK_FCT_DO( fd_msg_avp_new ( ta_sess_id, 0, &avp ), goto out ); - val.os.data = (uint8_t *)sid; - val.os.len = strlen(sid); + val.os.data = sid; + val.os.len = sidlen; CHECK_FCT_DO( fd_msg_avp_setvalue( avp, &val ), goto out ); CHECK_FCT_DO( fd_msg_avp_add( req, MSG_BRW_FIRST_CHILD, avp ), goto out );
--- a/extensions/test_app/ta_cli.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/test_app/ta_cli.c Wed Feb 09 15:26:58 2011 +0900 @@ -149,7 +149,8 @@ CHECK_FCT_DO( fd_msg_new( ta_cmd_r, MSGFL_ALLOC_ETEID, &req ), goto out ); /* Create a new session */ - CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, "app_test", 8 ), goto out ); + #define TEST_APP_SID_OPT "app_test" + CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)TEST_APP_SID_OPT, CONSTSTRLEN(TEST_APP_SID_OPT) ), goto out ); /* Create the random value to store with the session */ mi = malloc(sizeof(struct ta_mess_info)); @@ -164,11 +165,12 @@ /* Session-Id */ { - char * sid; - CHECK_FCT_DO( fd_sess_getsid ( sess, &sid ), goto out ); + os0_t sid; + size_t sidlen; + CHECK_FCT_DO( fd_sess_getsid ( sess, &sid, &sidlen ), goto out ); CHECK_FCT_DO( fd_msg_avp_new ( ta_sess_id, 0, &avp ), goto out ); - val.os.data = (uint8_t *)sid; - val.os.len = strlen(sid); + val.os.data = sid; + val.os.len = sidlen; CHECK_FCT_DO( fd_msg_avp_setvalue( avp, &val ), goto out ); CHECK_FCT_DO( fd_msg_avp_add( req, MSG_BRW_FIRST_CHILD, avp ), goto out );
--- a/extensions/test_netemul/tne_process.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/test_netemul/tne_process.c Wed Feb 09 15:26:58 2011 +0900 @@ -87,11 +87,12 @@ /* Duplicate eventually, unless deactivated */ if (tne_conf.dupl_proba != 0.0) { - char * src; + DiamId_t src; + size_t srclen; /* Pick a random value in [0, 1] */ double my_rand = drand48(); m = pi->chain.o; - CHECK_FCT( fd_msg_source_get(m, &src) ); + CHECK_FCT( fd_msg_source_get(m, &src, &srclen) ); while (my_rand < (double) tne_conf.dupl_proba) { /* create the duplicate */ @@ -104,7 +105,7 @@ /* Duplicate the message */ CHECK_FCT( fd_msg_bufferize(m, &buf, &len) ); CHECK_FCT( fd_msg_parse_buffer(&buf, len, &nm) ); - CHECK_FCT( fd_msg_source_set(nm, src, 0, NULL) ); + CHECK_FCT( fd_msg_source_set(nm, src, srclen, 0, NULL) ); CHECK_FCT( fd_msg_hdr(nm, &nh) ); nh->msg_flags |= CMD_FLAG_RETRANSMIT; /* Add the 'T' flag */ TRACE_DEBUG(FULL, "[tne] Duplicated message %p as %p", m, nm);
--- a/extensions/test_sip/locationinfo.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/test_sip/locationinfo.c Wed Feb 09 15:26:58 2011 +0900 @@ -61,12 +61,13 @@ // Create a new session { - CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, "appsip", 6 )); - char * sid; - CHECK_FCT( fd_sess_getsid ( sess, &sid )); + CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)"appsip", 6 )); + os0_t sid; + size_t sidlen; + CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen )); CHECK_FCT( fd_msg_avp_new ( sip_dict.Session_Id, 0, &avp )); - value.os.data = (uint8_t *)sid; - value.os.len = strlen(sid); + value.os.data = sid; + value.os.len = sidlen; CHECK_FCT( fd_msg_avp_setvalue( avp, &value )); CHECK_FCT( fd_msg_avp_add( message, MSG_BRW_FIRST_CHILD, avp )); }
--- a/extensions/test_sip/locationinfosl.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/test_sip/locationinfosl.c Wed Feb 09 15:26:58 2011 +0900 @@ -61,12 +61,13 @@ // Create a new session { - CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, "appsip", 6 )); - char * sid; - CHECK_FCT( fd_sess_getsid ( sess, &sid )); + CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)"appsip", 6 )); + os0_t sid; + size_t sidlen; + CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen )); CHECK_FCT( fd_msg_avp_new ( sip_dict.Session_Id, 0, &avp )); - value.os.data = (uint8_t *)sid; - value.os.len = strlen(sid); + value.os.data = sid; + value.os.len = sidlen; CHECK_FCT( fd_msg_avp_setvalue( avp, &value )); CHECK_FCT( fd_msg_avp_add( message, MSG_BRW_FIRST_CHILD, avp )); }
--- a/extensions/test_sip/serverassignment.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/test_sip/serverassignment.c Wed Feb 09 15:26:58 2011 +0900 @@ -70,12 +70,13 @@ // Create a new session { - CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, "appsip", 6 )); - char * sid; - CHECK_FCT( fd_sess_getsid ( sess, &sid )); + CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)"appsip", 6 )); + os0_t sid; + size_t sidlen; + CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen )); CHECK_FCT( fd_msg_avp_new ( sip_dict.Session_Id, 0, &avp )); - value.os.data = (uint8_t *)sid; - value.os.len = strlen(sid); + value.os.data = sid; + value.os.len = sidlen; CHECK_FCT( fd_msg_avp_setvalue( avp, &value )); CHECK_FCT( fd_msg_avp_add( message, MSG_BRW_FIRST_CHILD, avp )); }
--- a/extensions/test_sip/userauthorization.c Mon Jan 31 17:22:21 2011 +0900 +++ b/extensions/test_sip/userauthorization.c Wed Feb 09 15:26:58 2011 +0900 @@ -66,12 +66,13 @@ // Create a new session { - CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, "appsip", 6 )); - char * sid; - CHECK_FCT( fd_sess_getsid ( sess, &sid )); + CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)"appsip", 6 )); + os0_t sid; + size_t sidlen; + CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen )); CHECK_FCT( fd_msg_avp_new ( sip_dict.Session_Id, 0, &avp )); - value.os.data = (uint8_t *)sid; - value.os.len = strlen(sid); + value.os.data = sid; + value.os.len = sidlen; CHECK_FCT( fd_msg_avp_setvalue( avp, &value )); CHECK_FCT( fd_msg_avp_add( message, MSG_BRW_FIRST_CHILD, avp )); }
--- a/include/freeDiameter/libfdcore.h Mon Jan 31 17:22:21 2011 +0900 +++ b/include/freeDiameter/libfdcore.h Wed Feb 09 15:26:58 2011 +0900 @@ -107,9 +107,9 @@ char *cnf_file; /* Configuration file to parse, default is DEFAULT_CONF_FILE */ - char *cnf_diamid; /* Diameter Identity of the local peer (FQDN -- UTF-8) */ - size_t cnf_diamid_len; /* length of the previous string */ - char *cnf_diamrlm; /* Diameter realm of the local peer, default to realm part of diam_id */ + DiamId_t cnf_diamid; /* Diameter Identity of the local peer (FQDN -- ASCII) */ + size_t cnf_diamid_len;/* cached length of the previous string */ + DiamId_t cnf_diamrlm; /* Diameter realm of the local peer, default to realm part of cnf_diamid */ size_t cnf_diamrlm_len;/* length of the previous string */ unsigned int cnf_timer_tc; /* The value in seconds of the default Tc timer */ @@ -187,6 +187,10 @@ STATE_SUSPECT, /* A DWR was sent and not answered within TwTime. Failover in progress. */ STATE_REOPEN, /* Connection has been re-established, waiting for 3 DWR/DWA exchanges before putting back to service */ + /* Ordering issues with multistream & state machine. -- see top of p_psm.c for explanation */ + STATE_OPEN_NEW, /* after CEA is sent, until a new message is received. Force ordering in this state */ + STATE_CLOSING_GRACE, /* after DPA is sent or received, give a short delay for messages in the pipe to be received. */ + /* Error state */ STATE_ZOMBIE /* The PSM thread is not running anymore; it must be re-started or peer should be deleted. */ #define STATE_MAX STATE_ZOMBIE @@ -204,6 +208,8 @@ , "STATE_OPEN_HANDSHAKE" \ , "STATE_SUSPECT" \ , "STATE_REOPEN" \ + , "STATE_OPEN_NEW" \ + , "STATE_CLOSING_GRACE" \ , "STATE_ZOMBIE" \ }; extern const char *peer_state_str[]; @@ -236,7 +242,8 @@ /* Information about a remote peer */ struct peer_info { - char * pi_diamid; /* UTF-8, \0 terminated. The Diameter Identity of the remote peer. */ + DiamId_t pi_diamid; /* (supposedly) UTF-8, \0 terminated. The Diameter Identity of the remote peer. */ + size_t pi_diamidlen; /* cached length of pi_diamid */ struct { struct { @@ -249,7 +256,7 @@ } pic_flags; /* Flags influencing the connection to the remote peer */ - char * pic_realm; /* If configured, the daemon will match the received realm in CER/CEA matches this. */ + DiamId_t pic_realm; /* If configured, the daemon will check the received realm in CER/CEA matches this. */ uint16_t pic_port; /* port to connect to. 0: default. */ uint32_t pic_lft; /* lifetime of this peer when inactive (see pic_flags.exp definition) */ @@ -262,13 +269,15 @@ struct { - enum peer_state pir_state; /* Current state of the peer in the state machine. fd_cpu_flush_cache() might be useful before reading. */ + /* enum peer_state pir_state; */ + /* Since 1.1.0, read the state with fd_peer_getstate(peer). */ - char * pir_realm; /* The received realm in CER/CEA. */ + DiamId_t pir_realm; /* The received realm in CER/CEA. */ + size_t pir_realmlen; /* length of the realm */ uint32_t pir_vendorid; /* Content of the Vendor-Id AVP, or 0 by default */ uint32_t pir_orstate; /* Origin-State-Id value */ - char * pir_prodname; /* copy of UTF-8 Product-Name AVP (\0 terminated) */ + os0_t pir_prodname; /* copy of Product-Name AVP (\0 terminated) */ uint32_t pir_firmrev; /* Content of the Firmware-Revision AVP */ int pir_relay; /* The remote peer advertized the relay application */ struct fd_list pir_apps; /* applications advertised by the remote peer, except relay (pi_flags.relay) */ @@ -289,7 +298,7 @@ struct peer_hdr { - struct fd_list chain; /* List of all the peers, ordered by their Diameter Id */ + struct fd_list chain; /* Link into the list of all the peers, ordered by their Diameter Id (fd_os_cmp) */ struct peer_info info; /* The public data */ /* This header is followed by more data in the private peer structure definition */ @@ -338,7 +347,9 @@ * FUNCTION: fd_peer_getbyid * * PARAMETERS: - * diamid : A \0 terminated string. + * diamid : an UTF8 string describing the diameter Id of the peer to seek + * diamidlen : length of the diamid + * igncase : perform an almost-case-insensitive search? (slower) * peer : The peer is stored here if it exists. * * DESCRIPTION: @@ -348,7 +359,22 @@ * 0 : *peer has been updated (to NULL if the peer is not found). * !0 : An error occurred. */ -int fd_peer_getbyid( char * diamid, struct peer_hdr ** peer ); +int fd_peer_getbyid( DiamId_t diamid, size_t diamidlen, int igncase, struct peer_hdr ** peer ); + +/* + * FUNCTION: fd_peer_get_state + * + * PARAMETERS: + * peer : The peer which state to read + * + * DESCRIPTION: + * Returns the current state of the peer. + * + * RETURN VALUE: + * -1 : peer is invalid + * >=0 : the state of the peer at the time of reading. + */ +int fd_peer_get_state(struct peer_hdr *peer); /* * FUNCTION: fd_peer_validate_register @@ -474,6 +500,10 @@ /* Add Origin-Host, Origin-Realm, (if osi) Origin-State-Id AVPS at the end of the message */ int fd_msg_add_origin ( struct msg * msg, int osi ); +/* Generate a new Session-Id and add it at the beginning of the message (opt is added at the end of the sid if provided) */ +int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen ); + + /* Parse a message against our dictionary, and in case of error log and eventually build the error reply (on return and EBADMSG, *msg == NULL or *msg is the error message ready to send) */ int fd_msg_parse_or_error( struct msg ** msg );
--- a/include/freeDiameter/libfdproto.h Mon Jan 31 17:22:21 2011 +0900 +++ b/include/freeDiameter/libfdproto.h Wed Feb 09 15:26:58 2011 +0900 @@ -44,6 +44,7 @@ * The file contains the following parts: * DEBUG * MACROS + * OCTET STRINGS * THREADS * LISTS * DICTIONARY @@ -566,6 +567,64 @@ #define BUFSIZ 96 #endif /* BUFSIZ */ +/* This gives the length of a const string */ +#define CONSTSTRLEN( str ) (sizeof(str) - 1) + + +/*============================================================*/ +/* BINARY STRINGS */ +/*============================================================*/ + +/* Compute a hash value of a binary string. +The hash must remain local to this machine, there is no guarantee that same input +will give same output on a different system (endianness) */ +uint32_t fd_os_hash ( uint8_t * string, size_t len ); + +/* This type used for binary strings that contain no \0 except as their last character. +It means some string operations can be used on it. */ +typedef uint8_t * os0_t; + +/* Same as strdup but for os0_t strings */ +os0_t os0dup_int(os0_t s, size_t l); +#define os0dup( _s, _l) (void *)os0dup_int((os0_t)(_s), _l) + +/* Check that an octet string value can be used as os0_t */ +static __inline__ int fd_os_is_valid_os0(uint8_t * os, size_t oslen) { + /* The only situation where it is not valid is when it contains a \0 inside the octet string */ + return (memchr(os, '\0', oslen) == NULL); +} + +/* The following type denotes a verified DiameterIdentity value (that contains only pure letters, digits, hyphen, dot) */ +typedef char * DiamId_t; + +/* Maximum length of a hostname we accept */ +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 512 +#endif /* HOST_NAME_MAX */ + +/* Check if a binary string contains a valid Diameter Identity value. + rfc3588 states explicitely that such a Diameter Identity consists only of ASCII characters. */ +int fd_os_is_valid_DiameterIdentity(uint8_t * os, size_t ossz); + +/* This checks a string is a valid DiameterIdentity and applies IDNA transformations otherwise (xn--...) */ +int fd_os_validate_DiameterIdentity(char ** id, size_t * outsz, int memory /* 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static) */ ); + +/* Create an order relationship for binary strings (not needed to be \0 terminated). + It does NOT mimic strings relationships so that it is more efficient. It is case sensitive. + (the strings are actually first ordered by their lengh, then by their bytes contents) + returns: -1 if os1 < os2; +1 if os1 > os2; 0 if they are equal */ +int fd_os_cmp_int(os0_t os1, size_t os1sz, os0_t os2, size_t os2sz); +#define fd_os_cmp(_o1, _l1, _o2, _l2) fd_os_cmp_int((os0_t)(_o1), _l1, (os0_t)(_o2), _l2) + +/* A roughly case-insensitive variant, which actually only compares ASCII chars (0-127) in a case-insentitive maneer + -- this should be OK with (old) DNS names. Not sure how it works with IDN... + -- it also clearly does not support locales where a lowercase letter uses more space than upper case, such as ß -> ss + It is slower than fd_os_cmp... + This function should give the same order as fd_os_cmp, except when it finds 2 strings to be equal. + Note that the result is NOT the same as strcasecmp !!! + */ +int fd_os_almostcasecmp_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz); +#define fd_os_almostcasecmp(_o1, _l1, _o2, _l2) fd_os_almostcasecmp_int((os0_t)(_o1), _l1, (os0_t)(_o2), _l2) /*============================================================*/ @@ -601,13 +660,6 @@ return 0; } -/* Force flushing the cache of a CPU before reading a shared memory area (use only for atomic reads such as int and void*) */ -extern pthread_mutex_t fd_cpu_mtx_dummy; /* only for the macro bellow, so that we have reasonably fresh pir_state value when needed */ -#define fd_cpu_flush_cache() { \ - (void)pthread_mutex_lock(&fd_cpu_mtx_dummy); \ - (void)pthread_mutex_unlock(&fd_cpu_mtx_dummy); \ -} - /************* Cancelation cleanup handlers for common objects @@ -672,14 +724,6 @@ -/*============================================================*/ -/* HASH */ -/*============================================================*/ - -/* Compute a hash value of a string (session id, diameter id, ...) */ -uint32_t fd_hash ( char * string, size_t len ); - - /*============================================================*/ /* DICTIONARY */ @@ -801,13 +845,13 @@ /* Type to hold data associated to a vendor */ struct dict_vendor_data { vendor_id_t vendor_id; /* ID of a vendor */ - char *vendor_name; /* The name of this vendor */ + char * vendor_name; /* The name of this vendor */ }; /* The criteria for searching a vendor object in the dictionary */ enum { VENDOR_BY_ID = 10, /* "what" points to a vendor_id_t */ - VENDOR_BY_NAME, /* "what" points to a string */ + VENDOR_BY_NAME, /* "what" points to a char * */ VENDOR_OF_APPLICATION /* "what" points to a struct dict_object containing an application (see bellow) */ }; @@ -871,13 +915,13 @@ /* Type to hold data associated to an application */ struct dict_application_data { application_id_t application_id; /* ID of the application */ - char *application_name; /* The name of this application */ + char * application_name; /* The name of this application */ }; /* The criteria for searching an application object in the dictionary */ enum { APPLICATION_BY_ID = 20, /* "what" points to a application_id_t */ - APPLICATION_BY_NAME, /* "what" points to a string */ + APPLICATION_BY_NAME, /* "what" points to a char * */ APPLICATION_OF_TYPE, /* "what" points to a struct dict_object containing a type object (see bellow) */ APPLICATION_OF_COMMAND /* "what" points to a struct dict_object containing a command (see bellow) */ }; @@ -946,7 +990,7 @@ struct { uint8_t *data; /* bytes buffer */ size_t len; /* length of the data buffer */ - } os; /* Storage for an octet string, data is alloc'd and must be freed */ + } os; /* Storage for an octet string */ int32_t i32; /* integer 32 */ int64_t i64; /* integer 64 */ uint32_t u32; /* unsigned 32 */ @@ -1010,7 +1054,7 @@ /* Type to hold data associated to a derived AVP data type */ struct dict_type_data { enum dict_avp_basetype type_base; /* How the data of such AVP must be interpreted */ - char *type_name; /* The name of this type */ + char * type_name; /* The name of this type */ dict_avpdata_interpret type_interpret;/* cb to convert the AVP value in more comprehensive format (or NULL) */ dict_avpdata_encode type_encode; /* cb to convert formatted data into an AVP value (or NULL) */ void (*type_dump)(union avp_value * val, FILE * fstr); /* cb called by fd_msg_dump_one for this type of data (if != NULL), to dump the AVP value in fstr */ @@ -1018,7 +1062,7 @@ /* The criteria for searching a type object in the dictionary */ enum { - TYPE_BY_NAME = 30, /* "what" points to a string */ + TYPE_BY_NAME = 30, /* "what" points to a char * */ TYPE_OF_ENUMVAL, /* "what" points to a struct dict_object containing an enumerated constant (DICT_ENUMVAL, see bellow). */ TYPE_OF_AVP /* "what" points to a struct dict_object containing an AVP object. */ }; @@ -1067,7 +1111,7 @@ /* Type to hold data of named constants for AVP */ struct dict_enumval_data { - char *enum_name; /* The name of this constant */ + char * enum_name; /* The name of this constant */ union avp_value enum_value; /* Value of the constant. Union term depends on parent type's base type. */ }; @@ -1079,7 +1123,7 @@ struct dict_enumval_request { /* Identifier of the parent type, one of the following must not be NULL */ struct dict_object *type_obj; - char *type_name; + char * type_name; /* Search criteria for the constant */ struct dict_enumval_data search; /* search.enum_value is used only if search.enum_name == NULL */ @@ -1182,7 +1226,7 @@ struct dict_avp_data { avp_code_t avp_code; /* Code of the avp */ vendor_id_t avp_vendor; /* Vendor of the AVP, or 0 */ - char *avp_name; /* Name of this AVP */ + char * avp_name; /* Name of this AVP */ uint8_t avp_flag_mask; /* Mask of fixed AVP flags */ uint8_t avp_flag_val; /* Values of the fixed flags */ enum dict_avp_basetype avp_basetype; /* Basic type of data found in the AVP */ @@ -1191,7 +1235,7 @@ /* The criteria for searching an avp object in the dictionary */ enum { AVP_BY_CODE = 50, /* "what" points to an avp_code_t, vendor is always 0 */ - AVP_BY_NAME, /* "what" points to a string, vendor is always 0 */ + AVP_BY_NAME, /* "what" points to a char *, vendor is always 0 */ AVP_BY_CODE_AND_VENDOR, /* "what" points to a struct dict_avp_request (see bellow), where avp_vendor and avp_code are set */ AVP_BY_NAME_AND_VENDOR, /* "what" points to a struct dict_avp_request (see bellow), where avp_vendor and avp_name are set */ AVP_BY_NAME_ALL_VENDORS /* "what" points to a string. Might be quite slow... */ @@ -1201,7 +1245,7 @@ struct dict_avp_request { vendor_id_t avp_vendor; avp_code_t avp_code; - char *avp_name; + char * avp_name; }; @@ -1309,14 +1353,14 @@ /* Type to hold data associated to a command */ struct dict_cmd_data { command_code_t cmd_code; /* code of the command */ - char *cmd_name; /* Name of the command */ + char * cmd_name; /* Name of the command */ uint8_t cmd_flag_mask; /* Mask of fixed-value flags */ uint8_t cmd_flag_val; /* values of the fixed flags */ }; /* The criteria for searching an avp object in the dictionary */ enum { - CMD_BY_NAME = 60, /* "what" points to a string */ + CMD_BY_NAME = 60, /* "what" points to a char * */ CMD_BY_CODE_R, /* "what" points to a command_code_t. The "Request" command is returned. */ CMD_BY_CODE_A, /* "what" points to a command_code_t. The "Answer" command is returned. */ CMD_ANSWER /* "what" points to a struct dict_object of a request command. The corresponding "Answer" command is returned. */ @@ -1589,10 +1633,10 @@ * EINVAL : A parameter is invalid. * ENOMEM : Not enough memory to complete the operation */ -int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(session_state * state, char * sid, void * opaque), void * opaque ); +int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(session_state * state, os0_t sid, void * opaque), void * opaque ); /* Macro to avoid casting everywhere */ #define fd_sess_handler_create( _handler, _cleanup, _opaque ) \ - fd_sess_handler_create_internal( (_handler), (void (*)(session_state *, char *, void *))(_cleanup), (void *)(_opaque) ) + fd_sess_handler_create_internal( (_handler), (void (*)(session_state *, os0_t, void *))(_cleanup), (void *)(_opaque) ) /* @@ -1620,8 +1664,9 @@ * * PARAMETERS: * session : The location where the session object will be created upon success. - * diamId : \0-terminated string containing a Diameter Identity. - * opt : Additional string. Usage is described bellow. + * diamid : a Diameter Identity, or NULL. + * diamidlen : if diamid is \0-terminated, this can be 0. Otherwise, the length of diamid. + * opt : Additional string, or NULL. Usage is described bellow. * optlen : if opt is \0-terminated, this can be 0. Otherwise, the length of opt. * * DESCRIPTION: @@ -1638,13 +1683,13 @@ * EALREADY : A session with the same name already exists (returned in *session) * ENOMEM : Not enough memory to complete the operation */ -int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t optlen ); +int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, uint8_t * opt, size_t optlen ); /* * FUNCTION: fd_sess_fromsid * * PARAMETERS: - * sid : pointer to a string containing a Session-Id (UTF-8). + * sid : pointer to a string containing a Session-Id (should be UTF-8). * len : length of the sid string (which does not need to be '\0'-terminated) * session : On success, pointer to the session object created / retrieved. * isnew : if not NULL, set to 1 on return if the session object has been created, 0 if it was simply retrieved. @@ -1658,26 +1703,26 @@ * EINVAL : A parameter is invalid. * ENOMEM : Not enough memory to complete the operation */ -int fd_sess_fromsid ( char * sid, size_t len, struct session ** session, int * isnew); +int fd_sess_fromsid ( uint8_t * sid, size_t len, struct session ** session, int * isnew); /* * FUNCTION: fd_sess_getsid * * PARAMETERS: * session : Pointer to a session object. - * sid : On success, the location of a (\0-terminated) string is stored here. + * sid : On success, the location of the sid is stored here. * * DESCRIPTION: * Retrieve the session identifier (Session-Id) corresponding to a session object. - * The returned sid is an UTF-8 string terminated by \0, suitable for calls to strlen and strcpy. + * The returned sid is a \0-terminated binary string which might be UTF-8 (but there is no guarantee in the framework). * It may be used for example to set the value of an AVP. * Note that the sid string is not copied, just its reference... do not free it! * * RETURN VALUE: - * 0 : The sid parameter has been updated. + * 0 : The sid & len parameters have been updated. * EINVAL : A parameter is invalid. */ -int fd_sess_getsid ( struct session * session, char ** sid ); +int fd_sess_getsid ( struct session * session, os0_t * sid, size_t * sidlen ); /* * FUNCTION: fd_sess_settimeout @@ -1710,8 +1755,10 @@ * session : Pointer to a session object. * * DESCRIPTION: - * Destroys a session an all associated data, if any. + * Destroys all associated states of a session, if any. * Equivalent to a session timeout expired, but the effect is immediate. + * The session itself is marked as deleted, and will be freed when it is not referenced + * by any message anymore. * * RETURN VALUE: * 0 : The session no longer exists. @@ -1726,10 +1773,11 @@ * session : Pointer to a session object. * * DESCRIPTION: - * Destroys the resources of a session, only if no session_state is associated with it. + * Equivalent to fd_sess_destroy, only if no session_state is associated with the session. + * Otherwise, this function has no effect (except that it sets *session to NULL). * * RETURN VALUE: - * 0 : The session no longer exists. + * 0 : The session was reclaimed. * EINVAL : A parameter is invalid. */ int fd_sess_reclaim ( struct session ** session ); @@ -1801,27 +1849,29 @@ int fd_rtd_init(struct rt_data ** rtd); void fd_rtd_free(struct rt_data ** rtd); -/* Add a peer to the candidates list */ -int fd_rtd_candidate_add(struct rt_data * rtd, char * peerid, char * realm); +/* Add a peer to the candidates list. */ +int fd_rtd_candidate_add(struct rt_data * rtd, DiamId_t peerid, size_t peeridlen, DiamId_t realm, size_t realmlen); -/* Remove a peer from the candidates (if it is found) */ -void fd_rtd_candidate_del(struct rt_data * rtd, char * peerid, size_t sz /* if !0, peerid does not need to be \0 terminated */); +/* Remove a peer from the candidates (if it is found). The search is case-insensitive. */ +void fd_rtd_candidate_del(struct rt_data * rtd, uint8_t * id, size_t idsz); /* Extract the list of valid candidates, and initialize their scores to 0 */ void fd_rtd_candidate_extract(struct rt_data * rtd, struct fd_list ** candidates, int ini_score); /* If a peer returned a protocol error for this message, save it so that we don't try to send it there again */ -int fd_rtd_error_add(struct rt_data * rtd, char * sentto, uint8_t * origin, size_t originsz, uint32_t rcode); +int fd_rtd_error_add(struct rt_data * rtd, DiamId_t sentto, size_t senttolen, uint8_t * origin, size_t originsz, uint32_t rcode); /* The extracted list items have the following structure: */ struct rtd_candidate { struct fd_list chain; /* link in the list returned by the previous fct */ - char * diamid; /* the diameter Id of the peer */ - char * realm; /* the diameter realm of the peer (if known) */ + DiamId_t diamid; /* the diameter Id of the peer */ + size_t diamidlen; /* cached size of the diamid string */ + DiamId_t realm; /* the diameter realm of the peer */ + size_t realmlen; /* cached size of realm */ int score; /* the current routing score for this peer, see fd_rt_out_register definition for details */ }; -/* Reorder the list of peers */ +/* Reorder the list of peers by score */ int fd_rtd_candidate_reorder(struct fd_list * candidates); /* Note : it is fine for a callback to add a new entry in the candidates list after the list has been extracted. The diamid must then be malloc'd. */ @@ -2238,7 +2288,7 @@ * * PARAMETERS: * msg : A msg object. - * diamid : The diameter id of the peer from which this message was received. + * diamid,len : The diameter id of the peer from which this message was received. * add_rr : if true, a Route-Record AVP is added to the message with content diamid. In that case, dict must be supplied. * dict : a dictionary with definition of Route-Record AVP (if add_rr is true) * @@ -2251,8 +2301,8 @@ * 0 : Operation complete. * !0 : an error occurred. */ -int fd_msg_source_set( struct msg * msg, char * diamid, int add_rr, struct dictionary * dict ); -int fd_msg_source_get( struct msg * msg, char ** diamid ); +int fd_msg_source_set( struct msg * msg, DiamId_t diamid, size_t diamidlen, int add_rr, struct dictionary * dict ); +int fd_msg_source_get( struct msg * msg, DiamId_t *diamid, size_t * diamidlen ); /* * FUNCTION: fd_msg_eteid_get @@ -2369,7 +2419,7 @@ * EINVAL : The buffer does not contain a valid Diameter message. * ENOMEM : Unable to allocate enough memory to create the buffer object. */ -int fd_msg_bufferize ( struct msg * msg, unsigned char ** buffer, size_t * len ); +int fd_msg_bufferize ( struct msg * msg, uint8_t ** buffer, size_t * len ); /* * FUNCTION: fd_msg_parse_buffer @@ -2391,7 +2441,7 @@ * EBADMSG : The buffer does not contain a valid Diameter message (or is truncated). * EINVAL : A parameter is invalid. */ -int fd_msg_parse_buffer ( unsigned char ** buffer, size_t buflen, struct msg ** msg ); +int fd_msg_parse_buffer ( uint8_t ** buffer, size_t buflen, struct msg ** msg ); /* Parsing Error Information structure */ struct fd_pei { @@ -2653,6 +2703,7 @@ * msg : A msg object that have already been fd_msg_parse_dict. * session : The session corresponding to this object, if any. * action : Upon return, the action that must be taken on the message + * error_code : Upon return with action == DISP_ACT_ERROR, contains the error (such as "DIAMETER_UNABLE_TO_COMPLY") * * DESCRIPTION: * Call all handlers registered for a given message. @@ -2665,7 +2716,7 @@ * EINVAL : A parameter is invalid. * (other errors) */ -int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, const char ** error_code ); +int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, char ** error_code );
--- a/libfdcore/cnxctx.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/cnxctx.c Wed Feb 09 15:26:58 2011 +0900 @@ -50,8 +50,6 @@ * They are always oriented on connections (TCP or SCTP), connectionless modes (UDP or SCTP) are not supported. */ -/* Note: this file could be moved to libfreeDiameter instead, but since it uses gnuTLS we prefer to keep it in the daemon */ - /* Lifetime of a cnxctx object: * 1) Creation * a) a server socket: @@ -157,8 +155,7 @@ #ifdef DISABLE_SCTP TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled..."); ASSERT(0); - CHECK_FCT_DO( ENOTSUP, ); - return NULL; + CHECK_FCT_DO( ENOTSUP, return NULL); #else /* DISABLE_SCTP */ struct cnxctx * cnx = NULL; @@ -248,18 +245,18 @@ char portbuf[10]; int rc; - /* Numeric values for debug */ rc = getnameinfo((sSA *)&ss, sSAlen(&ss), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (rc) { snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); portbuf[0] = '\0'; } - snprintf(cli->cc_id, sizeof(cli->cc_id), "{%s} (%d) <- [%s]:%s (%d)", - IPPROTO_NAME(cli->cc_proto), serv->cc_socket, - addrbuf, portbuf, cli->cc_socket); + /* Numeric values for debug... */ + snprintf(cli->cc_id, sizeof(cli->cc_id), "%s from [%s]:%s (%d<-%d)", + IPPROTO_NAME(cli->cc_proto), addrbuf, portbuf, serv->cc_socket, cli->cc_socket); - /* Name for log messages */ + + /* ...Name for log messages */ rc = getnameinfo((sSA *)&ss, sSAlen(&ss), cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, 0); if (rc) snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc)); @@ -333,16 +330,16 @@ char portbuf[10]; int rc; - /* Numeric values for debug */ + /* Numeric values for debug... */ rc = getnameinfo(sa, addrlen, addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (rc) { snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); portbuf[0] = '\0'; } - snprintf(cnx->cc_id, sizeof(cnx->cc_id), "{TCP} -> [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket); + snprintf(cnx->cc_id, sizeof(cnx->cc_id), "TCP to [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket); - /* Name for log messages */ + /* ...Name for log messages */ rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); if (rc) snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc)); @@ -355,10 +352,9 @@ struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list) { #ifdef DISABLE_SCTP - TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled..."); + TRACE_DEBUG(INFO, "This function should never be called when SCTP is disabled..."); ASSERT(0); - CHECK_FCT_DO( ENOTSUP, ); - return NULL; + CHECK_FCT_DO( ENOTSUP, return NULL); #else /* DISABLE_SCTP */ int sock = 0; struct cnxctx * cnx = NULL; @@ -415,16 +411,16 @@ char portbuf[10]; int rc; - /* Numeric values for debug */ + /* Numeric values for debug... */ rc = getnameinfo((sSA *)&primary, sSAlen(&primary), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (rc) { snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); portbuf[0] = '\0'; } - snprintf(cnx->cc_id, sizeof(cnx->cc_id), "{SCTP} -> [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket); + snprintf(cnx->cc_id, sizeof(cnx->cc_id), "SCTP to [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket); - /* Name for log messages */ + /* ...Name for log messages */ rc = getnameinfo((sSA *)&primary, sSAlen(&primary), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); if (rc) snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc)); @@ -453,20 +449,63 @@ } /* Set the hostname to check during handshake */ -void fd_cnx_sethostname(struct cnxctx * conn, char * hn) +void fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn) { CHECK_PARAMS_DO( conn, return ); conn->cc_tls_para.cn = hn; } +/* We share a lock with many threads but we hold it only very short time so it is OK */ +static pthread_mutex_t state_lock = PTHREAD_MUTEX_INITIALIZER; +uint32_t fd_cnx_getstate(struct cnxctx * conn) +{ + uint32_t st; + CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } ); + st = conn->cc_state; + CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } ); + return st; +} +int fd_cnx_teststate(struct cnxctx * conn, uint32_t flag) +{ + uint32_t st; + CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } ); + st = conn->cc_state; + CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } ); + return st & flag; +} +void fd_cnx_addstate(struct cnxctx * conn, uint32_t orstate) +{ + CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } ); + conn->cc_state |= orstate; + CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } ); +} +void fd_cnx_setstate(struct cnxctx * conn, uint32_t abstate) +{ + CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } ); + conn->cc_state = abstate; + CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } ); +} + + /* Return the TLS state of a connection */ int fd_cnx_getTLS(struct cnxctx * conn) { CHECK_PARAMS_DO( conn, return 0 ); - fd_cpu_flush_cache(); - return conn->cc_status & CC_STATUS_TLS; + return fd_cnx_teststate(conn, CC_STATUS_TLS); } +/* Return true if the connection supports unordered delivery of messages */ +int fd_cnx_isMultichan(struct cnxctx * conn) +{ + CHECK_PARAMS_DO( conn, return 0 ); + #ifdef DISABLE_SCTP + if (conn->cc_proto == IPPROTO_SCTP) + return (conn->cc_sctp_para.str_in > 1) || (conn->cc_sctp_para.str_out > 1); + #endif /* DISABLE_SCTP */ + return 0; +} + + /* Get the list of endpoints (IP addresses) of the local and remote peers on this connection */ int fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps) { @@ -507,10 +546,11 @@ return conn->cc_remid; } -/* Retrieve a list of all IP addresses of the local system from the kernel, using */ +/* Retrieve a list of all IP addresses of the local system from the kernel, using getifaddrs */ int fd_cnx_get_local_eps(struct fd_list * list) { struct ifaddrs *iflist, *cur; + CHECK_SYS(getifaddrs(&iflist)); for (cur = iflist; cur != NULL; cur = cur->ifa_next) { @@ -542,22 +582,21 @@ TRACE_ENTRY("%p", conn); CHECK_PARAMS_DO( conn, goto fatal ); - TRACE_DEBUG(FULL, "Error flag set for socket %d (%s / %s)", conn->cc_socket, conn->cc_remid, conn->cc_id); + TRACE_DEBUG(FULL, "Error flag set for socket %d (%s, %s)", conn->cc_socket, conn->cc_id, conn->cc_remid); /* Mark the error */ - fd_cpu_flush_cache(); - conn->cc_status |= CC_STATUS_ERROR; + fd_cnx_addstate(conn, CC_STATUS_ERROR); /* Report the error if not reported yet, and not closing */ - if ((!(conn->cc_status & CC_STATUS_CLOSING )) && (!(conn->cc_status & CC_STATUS_SIGNALED ))) { + if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING | CC_STATUS_SIGNALED )) { TRACE_DEBUG(FULL, "Sending FDEVP_CNX_ERROR event"); - CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), goto fatal); - conn->cc_status |= CC_STATUS_SIGNALED; + CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_ERROR, 0, NULL), goto fatal); + fd_cnx_addstate(conn, CC_STATUS_SIGNALED); } - fd_cpu_flush_cache(); return; fatal: /* An unrecoverable error occurred, stop the daemon */ + ASSERT(0); CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); } @@ -582,8 +621,7 @@ ret = recv(conn->cc_socket, buffer, length, 0); /* Handle special case of timeout */ if ((ret < 0) && (errno == EAGAIN)) { - fd_cpu_flush_cache(); - if (! (conn->cc_status & CC_STATUS_CLOSING)) + if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) goto again; /* don't care, just ignore */ if (!timedout) { timedout ++; /* allow for one timeout while closing */ @@ -591,11 +629,11 @@ } } - CHECK_SYS_DO(ret, /* continue */); - /* Mark the error */ - if (ret <= 0) + if (ret <= 0) { + CHECK_SYS_DO(ret, /* continue, this is only used to log the error here */); fd_cnx_markerror(conn); + } return ret; } @@ -609,8 +647,7 @@ ret = send(conn->cc_socket, buffer, length, 0); /* Handle special case of timeout */ if ((ret < 0) && (errno == EAGAIN)) { - fd_cpu_flush_cache(); - if (! (conn->cc_status & CC_STATUS_CLOSING)) + if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) goto again; /* don't care, just ignore */ if (!timedout) { timedout ++; /* allow for one timeout while closing */ @@ -642,8 +679,8 @@ } ASSERT( conn->cc_proto == IPPROTO_TCP ); - ASSERT( ! (conn->cc_status & CC_STATUS_TLS) ); - ASSERT( Target_Queue(conn) ); + ASSERT( ! fd_cnx_teststate(conn, CC_STATUS_TLS ) ); + ASSERT( fd_cnx_target_queue(conn) ); /* Receive from a TCP connection: we have to rebuild the message boundaries */ do { @@ -665,7 +702,7 @@ length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3]; /* Check the received word is a valid begining of a Diameter message */ - if ((header[0] != DIAMETER_VERSION) /* defined in <libfreeDiameter.h> */ + if ((header[0] != DIAMETER_VERSION) /* defined in <libfdproto.h> */ || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */ /* The message is suspect */ TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length); @@ -690,8 +727,7 @@ } /* We have received a complete message, pass it to the daemon */ - fd_cpu_flush_cache(); - CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */); + CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */); } while (conn->cc_loop); @@ -725,12 +761,11 @@ } ASSERT( conn->cc_proto == IPPROTO_SCTP ); - ASSERT( ! (conn->cc_status & CC_STATUS_TLS) ); - ASSERT( Target_Queue(conn) ); + ASSERT( ! fd_cnx_teststate(conn, CC_STATUS_TLS ) ); + ASSERT( fd_cnx_target_queue(conn) ); do { - fd_cpu_flush_cache(); - CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, NULL, &buf, &bufsz, &event, &conn->cc_status), goto fatal ); + CHECK_FCT_DO( fd_sctp_recvmeta(conn, NULL, &buf, &bufsz, &event), goto fatal ); if (event == FDEVP_CNX_ERROR) { fd_cnx_markerror(conn); goto out; @@ -741,8 +776,7 @@ continue; } - fd_cpu_flush_cache(); - CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto fatal ); + CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, bufsz, buf), goto fatal ); } while (conn->cc_loop || (event != FDEVP_CNX_MSG_RECV)); @@ -762,7 +796,7 @@ { TRACE_ENTRY("%p %i", conn, loop); - CHECK_PARAMS( conn && Target_Queue(conn) && (!(conn->cc_status & CC_STATUS_TLS)) && (!conn->cc_loop)); + CHECK_PARAMS( conn && fd_cnx_target_queue(conn) && (!fd_cnx_teststate(conn, CC_STATUS_TLS)) && (!conn->cc_loop)); /* Release resources in case of a previous call was already made */ CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */); @@ -802,8 +836,7 @@ { switch (ret) { case GNUTLS_E_REHANDSHAKE: - fd_cpu_flush_cache(); - if (!(conn->cc_status & CC_STATUS_CLOSING)) + if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) CHECK_GNUTLS_DO( ret = gnutls_handshake(session), { if (TRACE_BOOL(INFO)) { @@ -814,8 +847,7 @@ case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: - fd_cpu_flush_cache(); - if (!(conn->cc_status & CC_STATUS_CLOSING)) + if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) goto again; TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now."); break; @@ -848,8 +880,7 @@ { switch (ret) { case GNUTLS_E_REHANDSHAKE: - fd_cpu_flush_cache(); - if (!(conn->cc_status & CC_STATUS_CLOSING)) + if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) CHECK_GNUTLS_DO( ret = gnutls_handshake(session), { if (TRACE_BOOL(INFO)) { @@ -860,8 +891,7 @@ case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: - fd_cpu_flush_cache(); - if (!(conn->cc_status & CC_STATUS_CLOSING)) + if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) goto again; TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_send now."); break; @@ -926,8 +956,7 @@ } /* We have received a complete message, pass it to the daemon */ - fd_cpu_flush_cache(); - CHECK_FCT_DO( ret = fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), + CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), { free(newmsg); CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); @@ -955,8 +984,8 @@ fd_log_threadname ( buf ); } - ASSERT( conn->cc_status & CC_STATUS_TLS ); - ASSERT( Target_Queue(conn) ); + ASSERT( fd_cnx_teststate(conn, CC_STATUS_TLS) ); + ASSERT( fd_cnx_target_queue(conn) ); /* The next function only returns when there is an error on the socket */ CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */); @@ -1214,7 +1243,7 @@ int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds) { TRACE_ENTRY( "%p %d %p %p", conn, mode, priority, alt_creds); - CHECK_PARAMS( conn && (!(conn->cc_status & CC_STATUS_TLS)) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) ); + CHECK_PARAMS( conn && (!fd_cnx_teststate(conn, CC_STATUS_TLS)) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) ); /* Save the mode */ conn->cc_tls_para.mode = mode; @@ -1247,9 +1276,8 @@ } /* Mark the connection as protected from here, so that the gnutls credentials will be freed */ - fd_cpu_flush_cache(); - conn->cc_status |= CC_STATUS_TLS; - + fd_cnx_addstate(conn, CC_STATUS_TLS); + /* Handshake master session */ { int ret; @@ -1298,7 +1326,7 @@ int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size) { TRACE_ENTRY("%p %p %p", conn, cert_list, cert_list_size); - CHECK_PARAMS( conn && (conn->cc_status & CC_STATUS_TLS) && cert_list && cert_list_size ); + CHECK_PARAMS( conn && fd_cnx_teststate(conn, CC_STATUS_TLS) && cert_list && cert_list_size ); /* This function only works for X.509 certificates. */ CHECK_PARAMS( gnutls_certificate_type_get (conn->cc_tls_para.session) == GNUTLS_CRT_X509 ); @@ -1359,16 +1387,29 @@ return EINVAL; } +/* Where the events are sent */ +struct fifo * fd_cnx_target_queue(struct cnxctx * conn) +{ + struct fifo *q; + CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } ); + q = conn->cc_alt ?: conn->cc_incoming; + CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } ); + return q; +} + /* Set an alternate FIFO list to send FDEVP_CNX_* events to */ int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo) { + int ret; TRACE_ENTRY( "%p %p", conn, alt_fifo ); CHECK_PARAMS( conn && alt_fifo && conn->cc_incoming ); /* The magic function does it all */ - CHECK_FCT( fd_fifo_move( conn->cc_incoming, alt_fifo, &conn->cc_alt ) ); + CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } ); + CHECK_FCT_DO( ret = fd_fifo_move( conn->cc_incoming, alt_fifo, &conn->cc_alt ), ); + CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } ); - return 0; + return ret; } /* Send function when no multi-stream is involved, or sending on stream #0 (send() always use stream 0)*/ @@ -1378,8 +1419,7 @@ size_t sent = 0; TRACE_ENTRY("%p %p %zd", conn, buf, len); do { - fd_cpu_flush_cache(); - if (conn->cc_status & CC_STATUS_TLS) { + if (fd_cnx_teststate(conn, CC_STATUS_TLS)) { CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent), ); } else { /* Maybe better to replace this call with sendmsg for atomic sending? */ @@ -1393,14 +1433,14 @@ return 0; } -/* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time, so we don't protect. */ +/* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time (on the same conn), so we don't protect. */ int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, uint32_t flags) { TRACE_ENTRY("%p %p %zd %x", conn, buf, len, flags); - CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! (conn->cc_status & CC_STATUS_ERROR)) && buf && len); + CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! fd_cnx_teststate(conn, CC_STATUS_ERROR)) && buf && len); - TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, (conn->cc_status & CC_STATUS_TLS) ? "TLS-protected ":"", conn->cc_id); + TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, fd_cnx_teststate(conn, CC_STATUS_TLS) ? "TLS-protected ":"", conn->cc_id); switch (conn->cc_proto) { case IPPROTO_TCP: @@ -1409,32 +1449,6 @@ #ifndef DISABLE_SCTP case IPPROTO_SCTP: { - if (flags & FD_CNX_BROADCAST) { - /* Send the buffer over all other streams */ - uint16_t str; - fd_cpu_flush_cache(); - if (conn->cc_status & CC_STATUS_TLS) { - for ( str=1; str < conn->cc_sctp_para.pairs; str++) { - ssize_t ret; - size_t sent = 0; - do { - CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctps_data.array[str].session, buf + sent, len - sent), ); - if (ret <= 0) - return ENOTCONN; - - sent += ret; - } while ( sent < len ); - } - } else { - for ( str=1; str < conn->cc_sctp_para.str_out; str++) { - CHECK_FCT_DO( fd_sctp_sendstr(conn->cc_socket, str, buf, len, &conn->cc_status), { fd_cnx_markerror(conn); return ENOTCONN; } ); - } - } - - /* Set the ORDERED flag also so that it is sent over stream 0 as well */ - flags &= FD_CNX_ORDERED; - } - if (flags & FD_CNX_ORDERED) { /* We send over stream #0 */ CHECK_FCT( send_simple(conn, buf, len) ); @@ -1443,18 +1457,18 @@ int another_str = 0; /* do we send over stream #0 ? */ - if ((conn->cc_sctp_para.str_out > 1) && ((! (conn->cc_status & CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1))) { + if ((conn->cc_sctp_para.str_out > 1) && ((!fd_cnx_teststate(conn, CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1))) { /* Update the id of the stream we will send this message over */ conn->cc_sctp_para.next += 1; - conn->cc_sctp_para.next %= ((conn->cc_status & CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out); + conn->cc_sctp_para.next %= (fd_cnx_teststate(conn, CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out); another_str = (conn->cc_sctp_para.next ? 1 : 0); } if ( ! another_str ) { CHECK_FCT( send_simple(conn, buf, len) ); } else { - if (!(conn->cc_status & CC_STATUS_TLS)) { - CHECK_FCT_DO( fd_sctp_sendstr(conn->cc_socket, conn->cc_sctp_para.next, buf, len, &conn->cc_status), { fd_cnx_markerror(conn); return ENOTCONN; } ); + if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) { + CHECK_FCT_DO( fd_sctp_sendstr(conn, conn->cc_sctp_para.next, buf, len), { fd_cnx_markerror(conn); return ENOTCONN; } ); } else { /* push the record to the appropriate session */ ssize_t ret; @@ -1495,24 +1509,23 @@ CHECK_PARAMS_DO(conn, return); - fd_cpu_flush_cache(); - conn->cc_status |= CC_STATUS_CLOSING; + fd_cnx_addstate(conn, CC_STATUS_CLOSING); /* Initiate shutdown of the TLS session(s): call gnutls_bye(WR), then read until error */ - if (conn->cc_status & CC_STATUS_TLS) { + if (fd_cnx_teststate(conn, CC_STATUS_TLS)) { #ifndef DISABLE_SCTP if (conn->cc_sctp_para.pairs > 1) { - if (! (conn->cc_status & CC_STATUS_ERROR )) { + if (! fd_cnx_teststate(conn, CC_STATUS_ERROR )) { /* Bye on master session */ CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) ); } - if (! (conn->cc_status & CC_STATUS_ERROR ) ) { + if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) { /* and other stream pairs */ fd_sctps_bye(conn); } - if (! (conn->cc_status & CC_STATUS_ERROR ) ) { + if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) { /* Now wait for all decipher threads to terminate */ fd_sctps_waitthreadsterm(conn); } else { @@ -1532,13 +1545,13 @@ } else { #endif /* DISABLE_SCTP */ - /* We are not using the sctps wrapper layer */ - if (! (conn->cc_status & CC_STATUS_ERROR ) ) { + /* We are TLS, but not using the sctps wrapper layer */ + if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) { /* Master session */ CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) ); } - if (! (conn->cc_status & CC_STATUS_ERROR ) ) { + if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) { /* In this case, just wait for thread rcvthr_tls_single to terminate */ if (conn->cc_rcvthr != (pthread_t)NULL) { CHECK_POSIX_DO( pthread_join(conn->cc_rcvthr, NULL), /* continue */ ); @@ -1554,7 +1567,6 @@ GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) ); conn->cc_tls_para.session = NULL; } - #ifndef DISABLE_SCTP } #endif /* DISABLE_SCTP */
--- a/libfdcore/cnxctx.h Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/cnxctx.h Wed Feb 09 15:26:58 2011 +0900 @@ -47,7 +47,8 @@ int cc_family; /* AF_INET or AF_INET6 (mixed) */ int cc_proto; /* IPPROTO_TCP or IPPROTO_SCTP */ - uint32_t cc_status; /* True if the object is being destroyed: we don't send events anymore */ + + uint32_t cc_state; /* True if the object is being destroyed: we don't send events anymore. access with fd_cnx_getstate() */ #define CC_STATUS_CLOSING 1 #define CC_STATUS_ERROR 2 #define CC_STATUS_SIGNALED 4 @@ -58,11 +59,10 @@ struct fifo * cc_incoming; /* FIFO queue of events received on the connection, FDEVP_CNX_* */ struct fifo * cc_alt; /* alternate fifo to send FDEVP_CNX_* events to. */ - #define Target_Queue(cnx) ((cnx)->cc_alt ?: (cnx)->cc_incoming) /* If cc_tls == true */ struct { - char *cn; /* If not NULL, remote certif will be checked to match this Common Name */ + DiamId_t cn; /* If not NULL, remote certif will be checked to match this Common Name */ int mode; /* GNUTLS_CLIENT / GNUTLS_SERVER */ gnutls_session_t session; /* Session object (stream #0 in case of SCTP) */ } cc_tls_para; @@ -83,6 +83,12 @@ }; void fd_cnx_markerror(struct cnxctx * conn); +uint32_t fd_cnx_getstate(struct cnxctx * conn); +int fd_cnx_teststate(struct cnxctx * conn, uint32_t flag); +void fd_cnx_addstate(struct cnxctx * conn, uint32_t orstate); +void fd_cnx_setstate(struct cnxctx * conn, uint32_t abstate); +struct fifo * fd_cnx_target_queue(struct cnxctx * conn); + /* Socket */ ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length); @@ -108,8 +114,8 @@ int fd_sctp_get_local_ep(int sock, struct fd_list * list); int fd_sctp_get_remote_ep(int sock, struct fd_list * list); int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary ); -int fd_sctp_sendstr(int sock, uint16_t strid, uint8_t * buf, size_t len, uint32_t * cc_closing); -int fd_sctp_recvmeta(int sock, uint16_t * strid, uint8_t ** buf, size_t * len, int *event, uint32_t * cc_closing); +int fd_sctp_sendstr(struct cnxctx * conn, uint16_t strid, uint8_t * buf, size_t len); +int fd_sctp_recvmeta(struct cnxctx * conn, uint16_t * strid, uint8_t ** buf, size_t * len, int *event); /* TLS over SCTP (multi-stream) */ struct sctps_ctx {
--- a/libfdcore/config.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/config.c Wed Feb 09 15:26:58 2011 +0900 @@ -145,6 +145,7 @@ int fd_conf_parse() { extern FILE * fddin; + char * orig = NULL; /* Attempt to find the configuration file */ if (!fd_g_config->cnf_file) @@ -153,14 +154,22 @@ fddin = fopen(fd_g_config->cnf_file, "r"); if ((fddin == NULL) && (*fd_g_config->cnf_file != '/')) { /* We got a relative path, attempt to add the default directory prefix */ - char * bkp = fd_g_config->cnf_file; - CHECK_MALLOC( fd_g_config->cnf_file = malloc(strlen(bkp) + strlen(DEFAULT_CONF_PATH) + 2) ); /* we will not free it, but not important */ - sprintf( fd_g_config->cnf_file, DEFAULT_CONF_PATH "/%s", bkp ); + orig = fd_g_config->cnf_file; + CHECK_MALLOC( fd_g_config->cnf_file = malloc(strlen(orig) + strlen(DEFAULT_CONF_PATH) + 2) ); /* we will not free it, but not important */ + sprintf( fd_g_config->cnf_file, DEFAULT_CONF_PATH "/%s", orig ); fddin = fopen(fd_g_config->cnf_file, "r"); } if (fddin == NULL) { int ret = errno; - fprintf(stderr, "Unable to open configuration file %s for reading: %s\n", fd_g_config->cnf_file, strerror(ret)); + if (orig) { + fprintf(stderr, "Unable to open configuration file for reading\n" + "Tried the following locations:\n" + " - %s\n" + " - %s\n" + "Error: %s\n", orig, fd_g_config->cnf_file, strerror(ret)); + } else { + fprintf(stderr, "Unable to open '%s' for reading: %s\n", fd_g_config->cnf_file, strerror(ret)); + } return ret; } @@ -177,11 +186,22 @@ return EINVAL; } + /* If the CA is not provided, let's use the same file (assuming self-signed certificate) */ + if (! fd_g_config->cnf_sec_data.ca_file) { + CHECK_MALLOC( fd_g_config->cnf_sec_data.ca_file = strdup(fd_g_config->cnf_sec_data.cert_file) ); + CHECK_GNUTLS_DO( fd_g_config->cnf_sec_data.ca_file_nr += gnutls_certificate_set_x509_trust_file( + fd_g_config->cnf_sec_data.credentials, + fd_g_config->cnf_sec_data.ca_file, + GNUTLS_X509_FMT_PEM), + { + TRACE_DEBUG(INFO, "Unable to use the local certificate as trusted security anchor (CA), please provide a valid TLS_CA='...' directive."); + return EINVAL; + } ); + } + + /* Resolve hostname if not provided */ if (fd_g_config->cnf_diamid == NULL) { -#ifndef HOST_NAME_MAX -#define HOST_NAME_MAX 1024 -#endif /* HOST_NAME_MAX */ char buf[HOST_NAME_MAX + 1]; struct addrinfo hints, *info; int ret; @@ -201,13 +221,13 @@ buf, gai_strerror(ret)); return EINVAL; } - CHECK_MALLOC( fd_g_config->cnf_diamid = strdup(info->ai_canonname) ); + fd_g_config->cnf_diamid = info->ai_canonname; + CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 1) ); freeaddrinfo(info); + } else { + CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 0) ); } - /* cache the length of the diameter id for the session module */ - fd_g_config->cnf_diamid_len = strlen(fd_g_config->cnf_diamid); - /* Handle the realm part */ if (fd_g_config->cnf_diamrlm == NULL) { char * start = NULL; @@ -219,11 +239,13 @@ "Please fix your Identity setting or provide Realm.\n", fd_g_config->cnf_diamid); return EINVAL; - } + } - CHECK_MALLOC( fd_g_config->cnf_diamrlm = strdup( start + 1 ) ); + fd_g_config->cnf_diamrlm = start + 1; + CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 1) ); + } else { + CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 0) ); } - fd_g_config->cnf_diamrlm_len = strlen(fd_g_config->cnf_diamrlm); /* Validate some flags */ if (fd_g_config->cnf_flags.no_ip4 && fd_g_config->cnf_flags.no_ip6) {
--- a/libfdcore/core.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/core.c Wed Feb 09 15:26:58 2011 +0900 @@ -44,7 +44,7 @@ /* gcrypt functions to support posix threads */ GCRY_THREAD_OPTION_PTHREAD_IMPL; -/* Signal extensions when the framework is completly initialized */ +/* Signal extensions when the framework is completly initialized (they are waiting in fd_core_waitstartcomplete()) */ static int is_ready = 0; static pthread_mutex_t is_ready_mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t is_ready_cnd = PTHREAD_COND_INITIALIZER; @@ -61,6 +61,8 @@ /* Thread that process incoming events on the main queue -- and terminates the framework when requested */ static pthread_t core_runner = (pthread_t)NULL; + +/* How the thread is terminated */ enum core_mode { CORE_MODE_EVENTS, CORE_MODE_IMMEDIATE
--- a/libfdcore/extensions.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/extensions.c Wed Feb 09 15:26:58 2011 +0900 @@ -56,7 +56,7 @@ { struct fd_ext_info * new; - TRACE_ENTRY("%p(%s) %p(%s)", filename, filename?filename:"", conffile, conffile?conffile:""); + TRACE_ENTRY("%p %p", filename, conffile); /* Check the filename is valid */ CHECK_PARAMS( filename ); @@ -105,7 +105,7 @@ #ifndef DEBUG ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL); #else /* DEBUG */ - /* We resolve immediatly so it's easier to find problems in ABI */ + /* We resolve symbols immediatly so it's easier to find problems in ABI */ ext->handler = dlopen(ext->filename, RTLD_NOW | RTLD_GLOBAL); #endif /* DEBUG */ if (ext->handler == NULL) {
--- a/libfdcore/fdcore-internal.h Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/fdcore-internal.h Wed Feb 09 15:26:58 2011 +0900 @@ -137,6 +137,10 @@ /* Origin of this peer object, for debug */ char *p_dbgorig; + /* State of the peer, and its lock */ + enum peer_state p_state; + pthread_mutex_t p_state_mtx; + /* Chaining in peers sublists */ struct fd_list p_actives; /* list of peers in the STATE_OPEN state -- used by routing */ struct fd_list p_expiry; /* list of expiring peers, ordered by their timeout value */ @@ -146,6 +150,7 @@ struct { unsigned pf_responder : 1; /* The peer has been created to handle incoming connection */ unsigned pf_delete : 1; /* Destroy the peer when the connection is terminated */ + unsigned pf_localterm : 1; /* If the latest DPR/DPA was initiated from this side */ unsigned pf_dw_pending : 1; /* A DWR message was sent and not answered yet */ @@ -192,6 +197,9 @@ #define CHECK_PEER( _p ) \ (((_p) != NULL) && (((struct fd_peer *)(_p))->p_eyec == EYEC_PEER)) +#define fd_peer_getstate(peer) fd_peer_get_state((struct peer_hdr *)(peer)) + + /* Events codespace for struct fd_peer->p_events */ enum { /* Dump all info about this peer in the debug log */ @@ -307,6 +315,7 @@ int fd_p_dw_reopen(struct fd_peer * peer); int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer); int fd_p_dp_initiate(struct fd_peer * peer, char * reason); +int fd_p_dp_newdelay(struct fd_peer * peer); /* Active peers -- routing process should only ever take the read lock, the write lock is managed by PSMs */ extern struct fd_list fd_g_activ_peers; @@ -326,11 +335,12 @@ struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa, socklen_t addrlen); struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list); int fd_cnx_start_clear(struct cnxctx * conn, int loop); -void fd_cnx_sethostname(struct cnxctx * conn, char * hn); +void fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn); int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds); char * fd_cnx_getid(struct cnxctx * conn); int fd_cnx_getproto(struct cnxctx * conn); int fd_cnx_getTLS(struct cnxctx * conn); +int fd_cnx_isMultichan(struct cnxctx * conn); int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size); int fd_cnx_get_local_eps(struct fd_list * list); int fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps); @@ -342,6 +352,5 @@ /* Flags for the fd_cnx_send function : */ #define FD_CNX_ORDERED (1 << 0) /* All messages sent with this flag set will be delivered in the same order. No guarantee on other messages */ -#define FD_CNX_BROADCAST (1 << 1) /* The message is sent over all stream pairs, in case of SCTP. No effect on TCP */ #endif /* _FDCORE_INTERNAL_H */
--- a/libfdcore/fdd.y Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/fdd.y Wed Feb 09 15:26:58 2011 +0900 @@ -235,7 +235,7 @@ appservthreads: APPSERVTHREADS '=' INTEGER ';' { - CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1024), + CHECK_PARAMS_DO( ($3 > 0) && ($3 < 256), { yyerror (&yylloc, conf, "Invalid value"); YYERROR; } ); conf->cnf_dispthr = (uint16_t)$3; }
--- a/libfdcore/messages.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/messages.c Wed Feb 09 15:26:58 2011 +0900 @@ -35,6 +35,7 @@ #include "fdcore-internal.h" +static struct dict_object * dict_avp_SI = NULL; /* Session-Id */ static struct dict_object * dict_avp_OH = NULL; /* Origin-Host */ static struct dict_object * dict_avp_OR = NULL; /* Origin-Realm */ static struct dict_object * dict_avp_EM = NULL; /* Error-Message */ @@ -53,6 +54,7 @@ TRACE_ENTRY(""); /* Initialize the dictionary objects that we may use frequently */ + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &dict_avp_SI , ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &dict_avp_OH , ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &dict_avp_OR , ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-State-Id", &fd_dict_avp_OSI , ENOENT) ); @@ -88,7 +90,7 @@ /* Set its value */ memset(&val, 0, sizeof(val)); - val.os.data = (unsigned char *)fd_g_config->cnf_diamid; + val.os.data = (os0_t)fd_g_config->cnf_diamid; val.os.len = fd_g_config->cnf_diamid_len; CHECK_FCT( fd_msg_avp_setvalue( avp_OH, &val ) ); @@ -101,7 +103,7 @@ /* Set its value */ memset(&val, 0, sizeof(val)); - val.os.data = (unsigned char *)fd_g_config->cnf_diamrlm; + val.os.data = (os0_t)fd_g_config->cnf_diamrlm; val.os.len = fd_g_config->cnf_diamrlm_len; CHECK_FCT( fd_msg_avp_setvalue( avp_OR, &val ) ); @@ -124,6 +126,43 @@ return 0; } +/* Create a new Session-Id and add at the beginning of the message. */ +int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen ) +{ + union avp_value val; + struct avp * avp = NULL; + struct session * sess = NULL; + os0_t sid; + size_t sidlen; + + TRACE_ENTRY("%p %p %zd", msg, opt, optlen); + CHECK_PARAMS( msg ); + + /* Check there is not already a session in the message */ + CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, NULL) ); + CHECK_PARAMS( sess == NULL ); + + /* Ok, now create the session */ + CHECK_FCT( fd_sess_new ( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, opt, optlen ) ); + CHECK_FCT( fd_sess_getsid( sess, &sid, &sidlen) ); + + /* Create an AVP to hold it */ + CHECK_FCT( fd_msg_avp_new( dict_avp_SI, 0, &avp ) ); + + /* Set its value */ + memset(&val, 0, sizeof(val)); + val.os.data = sid; + val.os.len = sidlen; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + + /* Add it to the message */ + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_FIRST_CHILD, avp ) ); + + /* Done! */ + return 0; +} + + /* Add Result-Code and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */ int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id ) { @@ -146,7 +185,7 @@ struct dict_enumval_request req; memset(&req, 0, sizeof(struct dict_enumval_request)); - /* First, get the enumerated type of the Result-Code AVP */ + /* First, get the enumerated type of the Result-Code AVP (this is fast, no need to cache the object) */ CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &(req.type_obj), ENOENT ) ); /* Now search for the value given as parameter */ @@ -183,7 +222,7 @@ /* Set its value */ memset(&val, 0, sizeof(val)); - val.os.data = (unsigned char *)fd_g_config->cnf_diamid; + val.os.data = (uint8_t *)fd_g_config->cnf_diamid; val.os.len = fd_g_config->cnf_diamid_len; CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) ); @@ -245,10 +284,10 @@ memset(&val, 0, sizeof(val)); if (errormsg) { - val.os.data = (unsigned char *)errormsg; + val.os.data = (uint8_t *)errormsg; val.os.len = strlen(errormsg); } else { - val.os.data = (unsigned char *)rescode; + val.os.data = (uint8_t *)rescode; val.os.len = strlen(rescode); } CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) ); @@ -310,7 +349,7 @@ ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei); if ((ret != EBADMSG) /* Parsing grouped AVP failed / Conflicting rule found */ && (ret != ENOTSUP)) /* Command is not supported / Mandatory AVP is not supported */ - return ret; + return ret; /* 0 or another error */ TRACE_DEBUG(INFO, "A message does not comply to the dictionary and/or rules (%s)", pei.pei_errcode); fd_msg_dump_walk(FULL, m);
--- a/libfdcore/p_ce.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/p_ce.c Wed Feb 09 15:26:58 2011 +0900 @@ -37,13 +37,6 @@ /* This file contains code to handle Capabilities Exchange messages (CER and CEA) and election process */ -/* Compilation option: - USE_CEA_BROADCAST - Define this to enable sending multiple copies of the CEA in case of SCTP connection. - This avoids a race condition when sending an application message over a different stream - than the CEA, it might be delivered first and thus ignored. -*/ - /* Save a connection as peer's principal */ static int set_peer_cnx(struct fd_peer * peer, struct cnxctx **cnx) { @@ -87,7 +80,7 @@ } } -/* Election: compare the Diameter Ids, return true if the election is won */ +/* Election: compare the Diameter Ids by lexical order, return true if the election is won */ static __inline__ int election_result(struct fd_peer * peer) { int ret = (strcasecmp(peer->p_hdr.info.pi_diamid, fd_g_config->cnf_diamid) < 0); @@ -244,26 +237,28 @@ /* Remove any information saved from a previous CER/CEA exchange */ static void cleanup_remote_CE_info(struct fd_peer * peer) { + /* free linked information */ free(peer->p_hdr.info.runtime.pir_realm); - peer->p_hdr.info.runtime.pir_realm = NULL; - peer->p_hdr.info.runtime.pir_vendorid = 0; - peer->p_hdr.info.runtime.pir_orstate = 0; free(peer->p_hdr.info.runtime.pir_prodname); - peer->p_hdr.info.runtime.pir_prodname = NULL; - peer->p_hdr.info.runtime.pir_firmrev = 0; - peer->p_hdr.info.runtime.pir_relay = 0; - peer->p_hdr.info.runtime.pir_isi = 0; while (!FD_IS_LIST_EMPTY(&peer->p_hdr.info.runtime.pir_apps)) { struct fd_list * li = peer->p_hdr.info.runtime.pir_apps.next; fd_list_unlink(li); free(li); } + /* note: pir_cert_list needs not be freed (belongs to gnutls) */ - fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV /* Remove previously advertised endpoints */ ); + /* cleanup the area */ + memset(&peer->p_hdr.info.runtime, 0, sizeof(peer->p_hdr.info.runtime)); + + /* reinit the list */ + fd_list_init(&peer->p_hdr.info.runtime.pir_apps, peer); + + /* Remove previously advertised endpoints */ + fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV ); } /* Extract information sent by the remote peer and save it in our peer structure */ -static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, char ** error_code, uint32_t *rc) +static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, struct fd_pei * error, uint32_t *rc) { struct avp * avp = NULL; @@ -308,11 +303,13 @@ } /* We check that the value matches what we know, otherwise disconnect the peer */ - /* here also, using strcasecmp on (supposed) UTF8 data might be bad idea... to be improved */ - if (strncasecmp((char *)hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, hdr->avp_value->os.len)) { + if (fd_os_almostcasecmp(hdr->avp_value->os.data, hdr->avp_value->os.len, + peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen)) { TRACE_DEBUG(INFO, "Received a message with Origin-Host set to '%.*s' while expecting '%s'\n", hdr->avp_value->os.len, hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid); - *error_code = "DIAMETER_UNKNOWN_PEER"; + error->pei_errcode = "ER_DIAMETER_AVP_NOT_ALLOWED"; + error->pei_message = "Your Origin-Host value does not match my configuration."; + error->pei_avp = avp; return EINVAL; } @@ -329,13 +326,24 @@ /* In case of multiple AVPs */ if (peer->p_hdr.info.runtime.pir_realm) { - TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-Realm AVP"); - goto next; + TRACE_DEBUG(INFO, "Multiple instances of the Origin-Realm AVP"); + error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"; + error->pei_message = "I found several Origin-Realm AVPs"; + error->pei_avp = avp; + return EINVAL; } - /* Save the value -- we don't change the case to avoid risking breaking UTF-8 with poor tolower() impls. */ - CHECK_MALLOC( peer->p_hdr.info.runtime.pir_realm = calloc( hdr->avp_value->os.len + 1, 1 ) ); - memcpy(peer->p_hdr.info.runtime.pir_realm, hdr->avp_value->os.data, hdr->avp_value->os.len); + /* If the octet string contains a \0 */ + if (!fd_os_is_valid_DiameterIdentity(hdr->avp_value->os.data, hdr->avp_value->os.len)) { + error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE"; + error->pei_message = "Your Origin-Realm contains invalid characters."; + error->pei_avp = avp; + return EINVAL; + } + + /* Save the value */ + CHECK_MALLOC( peer->p_hdr.info.runtime.pir_realm = os0dup( hdr->avp_value->os.data, hdr->avp_value->os.len ) ); + peer->p_hdr.info.runtime.pir_realmlen = hdr->avp_value->os.len; break; case AC_HOST_IP_ADDRESS: /* Host-IP-Address */ @@ -351,7 +359,13 @@ /* Get the sockaddr value */ memset(&ss, 0, sizeof(ss)); - CHECK_FCT( fd_msg_avp_value_interpret( avp, &ss) ); + CHECK_FCT_DO( fd_msg_avp_value_interpret( avp, &ss), + { + /* in case of error, assume the AVP value was wrong */ + error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE"; + error->pei_avp = avp; + return EINVAL; + } ); /* Save this endpoint in the list as advertized */ CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, (sSA *)&ss, sizeof(sSS), EP_FL_ADV ) ); @@ -369,8 +383,11 @@ /* In case of multiple AVPs */ if (peer->p_hdr.info.runtime.pir_vendorid) { - TRACE_DEBUG(INFO, "Ignored multiple instances of the Vendor-Id AVP"); - goto next; + TRACE_DEBUG(INFO, "Multiple instances of the Vendor-Id AVP"); + error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"; + error->pei_message = "I found several Vendor-Id AVPs"; + error->pei_avp = avp; + return EINVAL; } peer->p_hdr.info.runtime.pir_vendorid = hdr->avp_value->u32; @@ -387,8 +404,11 @@ /* In case of multiple AVPs */ if (peer->p_hdr.info.runtime.pir_prodname) { - TRACE_DEBUG(INFO, "Ignored multiple instances of the Product-Name AVP"); - goto next; + TRACE_DEBUG(INFO, "Multiple instances of the Product-Name AVP"); + error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"; + error->pei_message = "I found several Product-Name AVPs"; + error->pei_avp = avp; + return EINVAL; } CHECK_MALLOC( peer->p_hdr.info.runtime.pir_prodname = calloc( hdr->avp_value->os.len + 1, 1 ) ); @@ -406,8 +426,11 @@ /* In case of multiple AVPs */ if (peer->p_hdr.info.runtime.pir_orstate) { - TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-State-Id AVP"); - goto next; + TRACE_DEBUG(INFO, "Multiple instances of the Origin-State-Id AVP"); + error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"; + error->pei_message = "I found several Origin-State-Id AVPs"; + error->pei_avp = avp; + return EINVAL; } peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32; @@ -422,7 +445,10 @@ goto next; } - TRACE_DEBUG(FULL, "'%s' supports a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32); + TRACE_DEBUG(FULL, "'%s' claims support for a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32); + /* not that it makes a difference for us... + -- if an application actually needs this info, we could save it somewhere. + */ break; case AC_VENDOR_SPECIFIC_APPLICATION_ID: /* Vendor-Specific-Application-Id (grouped)*/ @@ -476,6 +502,9 @@ if (auth + acct != 1) { TRACE_DEBUG(FULL, "Invalid Vendor-Specific-Application-Id AVP received, ignored"); fd_msg_dump_one(FULL, avp); + error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE"; + error->pei_avp = avp; + return EINVAL; } else { /* Add an entry in the list */ CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, aid, vid, auth, acct) ); @@ -509,9 +538,9 @@ } if (hdr->avp_value->u32 == AI_RELAY) { + /* Not clear if the relay application can be inside this AVP... */ peer->p_hdr.info.runtime.pir_relay = 1; } else { - /* Not clear if the relay application can be inside this AVP... */ CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 0, 1) ); } break; @@ -536,7 +565,12 @@ ASSERT(0); /* To check if this really happens, and understand why... */ goto next; } - ASSERT( hdr->avp_value->u32 < 32 ); /* if false, we have to change the code bellow */ + if (hdr->avp_value->u32 >= 32 ) { + error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE"; + error->pei_message = "I don't support this Inband-Security-Id value (yet)."; + error->pei_avp = avp; + return EINVAL; + } peer->p_hdr.info.runtime.pir_isi |= (1 << hdr->avp_value->u32); break; } @@ -558,9 +592,9 @@ /* Find CER dictionary object and create an instance */ CHECK_FCT( fd_msg_new ( fd_dict_cmd_CER, MSGFL_ALLOC_ETEID, cer ) ); - /* Do we need Inband-Security-Id AVPs ? */ + /* Do we need Inband-Security-Id AVPs ? If we're already using TLS, we don't... */ if (!fd_cnx_getTLS(cnx)) { - isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it event if the peer does not use the old mechanism */ + isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it even if the peer does not use the old mechanism, it is impossible to distinguish */ isi_tls = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD; } @@ -586,16 +620,17 @@ } /* Reject an incoming connection attempt */ -static void receiver_reject(struct cnxctx * recv_cnx, struct msg ** cer, char * rescode, char * errormsg) +static void receiver_reject(struct cnxctx ** recv_cnx, struct msg ** cer, struct fd_pei * error) { /* Create and send the CEA with appropriate error code */ CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ), goto destroy ); - CHECK_FCT_DO( fd_msg_rescode_set(*cer, rescode, errormsg, NULL, 1 ), goto destroy ); - CHECK_FCT_DO( fd_out_send(cer, recv_cnx, NULL, FD_CNX_ORDERED), goto destroy ); + CHECK_FCT_DO( fd_msg_rescode_set(*cer, error->pei_errcode, error->pei_message, error->pei_avp, 1 ), goto destroy ); + CHECK_FCT_DO( fd_out_send(cer, *recv_cnx, NULL, FD_CNX_ORDERED), goto destroy ); /* And now destroy this connection */ destroy: - fd_cnx_destroy(recv_cnx); + fd_cnx_destroy(*recv_cnx); + *recv_cnx = NULL; if (*cer) { fd_msg_log(FD_MSG_LOG_DROPPED, *cer, "An error occurred while rejecting a CER."); fd_msg_free(*cer); @@ -613,8 +648,7 @@ CHECK_FCT( fd_out_send(&cer, initiator, peer, FD_CNX_ORDERED) ); /* Are we doing an election ? */ - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state == STATE_WAITCNXACK_ELEC) { + if (fd_peer_getstate(peer) == STATE_WAITCNXACK_ELEC) { if (election_result(peer)) { /* Close initiator connection */ fd_cnx_destroy(initiator); @@ -623,10 +657,12 @@ CHECK_FCT( fd_p_ce_process_receiver(peer) ); } else { + struct fd_pei pei; + memset(&pei, 0, sizeof(pei)); + pei.pei_errcode = "ELECTION_LOST"; /* Answer an ELECTION LOST to the receiver side */ - receiver_reject(peer->p_receiver, &peer->p_cer, "ELECTION_LOST", NULL); - peer->p_receiver = NULL; + receiver_reject(&peer->p_receiver, &peer->p_cer, &pei); CHECK_FCT( to_waitcea(peer, initiator) ); } } else { @@ -640,8 +676,10 @@ /* We have received a Capabilities Exchange message on the peer connection */ int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer) { - char * ec; uint32_t rc = 0; + int st; + struct fd_pei pei; + TRACE_ENTRY("%p %p", msg, peer); CHECK_PARAMS( msg && *msg && CHECK_PEER(peer) ); @@ -655,17 +693,16 @@ CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, MSGFL_ANSW_ERROR ) ); /* Set the error code */ - CHECK_FCT( fd_msg_rescode_set(*msg, "DIAMETER_COMMAND_UNSUPPORTED", "No CER allowed in current state", NULL, 1 ) ); + CHECK_FCT( fd_msg_rescode_set(*msg, "ER_DIAMETER_UNABLE_TO_COMPLY", "No CER allowed in current state", NULL, 1 ) ); /* msg now contains an answer message to send back */ CHECK_FCT_DO( fd_out_send(msg, NULL, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ ); } /* If the state is not WAITCEA, just discard the message */ - fd_cpu_flush_cache(); - if (req || (peer->p_hdr.info.runtime.pir_state != STATE_WAITCEA)) { + if (req || ((st = fd_peer_getstate(peer)) != STATE_WAITCEA)) { if (*msg) { - fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Received CER/CEA while in '%s' state.\n", STATE_STR(peer->p_hdr.info.runtime.pir_state)); + fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Received CER/CEA while in '%s' state.\n", STATE_STR(st)); CHECK_FCT_DO( fd_msg_free(*msg), /* continue */); *msg = NULL; } @@ -673,8 +710,10 @@ return 0; } + memset(&pei, 0, sizeof(pei)); + /* Save info from the CEA into the peer */ - CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &ec, &rc), goto cleanup ); + CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &pei, &rc), goto cleanup ); /* Dispose of the message, we don't need it anymore */ CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ ); @@ -700,7 +739,7 @@ default: /* In any other case, we abort all attempts to connect to this peer */ - TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code AVP %d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc); + TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code %d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc); return EINVAL; } @@ -750,13 +789,14 @@ return 0; } -/* Handle the receiver side to go to OPEN state (any election is resolved) */ +/* Handle the receiver side to go to OPEN or OPEN_NEW state (any election is resolved) */ int fd_p_ce_process_receiver(struct fd_peer * peer) { - char * ec = NULL; + struct fd_pei pei; struct msg * msg = NULL; int isi = 0; int fatal = 0; + int tls_sync=0; TRACE_ENTRY("%p", peer); @@ -764,15 +804,27 @@ msg = peer->p_cer; peer->p_cer = NULL; + memset(&pei, 0, sizeof(pei)); + /* Parse the content of the received CER */ - CHECK_FCT_DO( save_remote_CE_info(msg, peer, &ec, NULL), goto error_abort ); + CHECK_FCT_DO( save_remote_CE_info(msg, peer, &pei, NULL), goto error_abort ); + + /* Validate the realm if needed */ + if (peer->p_hdr.info.config.pic_realm) { + size_t len = strlen(peer->p_hdr.info.config.pic_realm); + if (fd_os_almostcasecmp(peer->p_hdr.info.config.pic_realm, len, peer->p_hdr.info.runtime.pir_realm, peer->p_hdr.info.runtime.pir_realmlen)) { + TRACE_DEBUG(INFO, "Rejected CER from peer '%s', realm mismatch with configured value (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid); + pei.pei_errcode = "DIAMETER_UNKNOWN_PEER"; /* maybe AVP_NOT_ALLOWED would be better fit? */ + goto error_abort; + } + } /* Validate the peer if needed */ if (peer->p_flags.pf_responder) { int res = fd_peer_validate( peer ); if (res < 0) { TRACE_DEBUG(INFO, "Rejected CER from peer '%s', validation failed (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid); - ec = "DIAMETER_UNKNOWN_PEER"; + pei.pei_errcode = "DIAMETER_UNKNOWN_PEER"; goto error_abort; } CHECK_FCT( res ); @@ -784,7 +836,7 @@ CHECK_FCT( fd_app_check_common( &fd_g_config->cnf_apps, &peer->p_hdr.info.runtime.pir_apps, &got_common) ); if (!got_common) { TRACE_DEBUG(INFO, "No common application with peer '%s', sending DIAMETER_NO_COMMON_APPLICATION", peer->p_hdr.info.pi_diamid); - ec = "DIAMETER_NO_COMMON_APPLICATION"; + pei.pei_errcode = "DIAMETER_NO_COMMON_APPLICATION"; fatal = 1; goto error_abort; } @@ -834,7 +886,7 @@ /* If we did not find an agreement */ if (!isi) { TRACE_DEBUG(INFO, "No common security mechanism with '%s', sending DIAMETER_NO_COMMON_SECURITY", peer->p_hdr.info.pi_diamid); - ec = "DIAMETER_NO_COMMON_SECURITY"; + pei.pei_errcode = "DIAMETER_NO_COMMON_SECURITY"; fatal = 1; goto error_abort; } @@ -848,11 +900,7 @@ CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, 0 ) ); CHECK_FCT( fd_msg_rescode_set(msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) ); CHECK_FCT( add_CE_info(msg, peer->p_cnxctx, isi & PI_SEC_TLS_OLD, isi & PI_SEC_NONE) ); -#ifdef USE_CEA_BROADCAST - CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, (isi & PI_SEC_TLS_OLD) ? FD_CNX_ORDERED : FD_CNX_BROADCAST) ); /* Broadcast in order to avoid further messages sent over a different stream be delivered first... */ -#else /* USE_CEA_BROADCAST */ CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED ) ); -#endif /* USE_CEA_BROADCAST */ /* Handshake if needed */ if (isi & PI_SEC_TLS_OLD) { @@ -877,7 +925,7 @@ return 0; } ); } - + tls_sync = 1; } else { if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) { TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid); @@ -890,22 +938,23 @@ fd_psm_change_state(peer, STATE_REOPEN ); CHECK_FCT( fd_p_dw_reopen(peer) ); } else { - fd_psm_change_state(peer, STATE_OPEN ); - fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw); + if ((!tls_sync) && (fd_cnx_isMultichan(peer->p_cnxctx))) { + fd_psm_change_state(peer, STATE_OPEN_NEW ); + /* send DWR */ + CHECK_FCT( fd_p_dw_timeout(peer) ); + } else { + + fd_psm_change_state(peer, STATE_OPEN ); + fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw); + } } return 0; error_abort: - if (ec) { - /* Create the error message */ - CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ) ); - - /* Set the error code */ - CHECK_FCT( fd_msg_rescode_set(msg, ec, NULL, NULL, 1 ) ); - - /* msg now contains an answer message to send back */ - CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ ); + if (pei.pei_errcode) { + /* Send the error */ + receiver_reject(&peer->p_cnxctx, &msg, &pei); } cleanup: @@ -924,8 +973,11 @@ /* We have received a CER on a new connection for this peer */ int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid) { - fd_cpu_flush_cache(); - switch (peer->p_hdr.info.runtime.pir_state) { + struct fd_pei pei; + int cur_state = fd_peer_getstate(peer); + memset(&pei, 0, sizeof(pei)); + + switch (cur_state) { case STATE_CLOSED: peer->p_receiver = *cnx; *cnx = NULL; @@ -959,14 +1011,16 @@ } else { /* Answer an ELECTION LOST to the receiver side and continue */ - receiver_reject(*cnx, msg, "ELECTION_LOST", "Please answer my CER instead, you won the election."); - *cnx = NULL; + pei.pei_errcode = "ELECTION_LOST"; + pei.pei_message = "Please answer my CER instead, you won the election."; + receiver_reject(cnx, msg, &pei); } break; default: - receiver_reject(*cnx, msg, "DIAMETER_UNABLE_TO_COMPLY", "Invalid state to receive a new connection attempt"); - *cnx = NULL; + pei.pei_errcode = "DIAMETER_UNABLE_TO_COMPLY"; /* INVALID COMMAND? in case of Capabilities-Updates? */ + pei.pei_message = "Invalid state to receive a new connection attempt."; + receiver_reject(cnx, msg, &pei); } return 0;
--- a/libfdcore/p_cnx.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/p_cnx.c Wed Feb 09 15:26:58 2011 +0900 @@ -35,6 +35,9 @@ #include "fdcore-internal.h" + +/* TODO: change the behavior to handle properly forced ordering at beginning & end of OPEN state */ + /* This file contains code used by a peer state machine to initiate a connection to remote peer */ struct next_conn { @@ -86,7 +89,7 @@ hints.ai_flags = AI_ADDRCONFIG; ret = getaddrinfo(peer->p_hdr.info.pi_diamid, NULL, &hints, &ai); if (ret) { - fd_log_debug("Unable to resolve address for peer '%s' (%s), aborting\n", peer->p_hdr.info.pi_diamid, gai_strerror(ret)); + TRACE_DEBUG(INFO, "Unable to resolve address for peer '%s' (%s), aborting\n", peer->p_hdr.info.pi_diamid, gai_strerror(ret)); if (ret != EAI_AGAIN) fd_psm_terminate( peer, NULL ); return 0; @@ -122,7 +125,8 @@ /* Now check we have at least one address to attempt */ if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) { - fd_log_debug("No address %savailable to connect to peer '%s', aborting\n", peer->p_hdr.info.config.pic_flags.pro3 ? "in the configured family " : "", peer->p_hdr.info.pi_diamid); + TRACE_DEBUG(INFO, "No address %savailable to connect to peer '%s', aborting\n", + peer->p_hdr.info.config.pic_flags.pro3 ? "in the configured family " : "", peer->p_hdr.info.pi_diamid); fd_psm_terminate( peer, NULL ); return 0; } @@ -218,7 +222,7 @@ /* Set the thread name */ { char buf[48]; - sprintf(buf, "ConnTo:%.*s", (int)(sizeof(buf)) - 8, peer->p_hdr.info.pi_diamid); + snprintf(buf, sizeof(buf), "ConnTo:%s", peer->p_hdr.info.pi_diamid); fd_log_threadname ( buf ); } @@ -246,7 +250,8 @@ break; #ifndef DISABLE_SCTP case IPPROTO_SCTP: - cnx = fd_cnx_cli_connect_sctp((peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ?: fd_g_config->cnf_flags.no_ip6, nc->port, &peer->p_hdr.info.pi_endpoints); + cnx = fd_cnx_cli_connect_sctp((peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ?: fd_g_config->cnf_flags.no_ip6, + nc->port, &peer->p_hdr.info.pi_endpoints); break; #endif /* DISABLE_SCTP */ } @@ -259,7 +264,7 @@ pthread_testcancel(); - } while (!cnx); /* and until cancellation */ + } while (!cnx); /* and until cancellation or all addresses attempted without success */ /* Now, we have an established connection in cnx */ @@ -273,7 +278,7 @@ CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL), { /* Handshake failed ... */ - fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid); + TRACE_DEBUG(INFO, "TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid); fd_cnx_destroy(cnx); empty_connection_list(peer); fd_ep_filter(&peer->p_hdr.info.pi_endpoints, EP_FL_CONF);
--- a/libfdcore/p_dp.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/p_dp.c Wed Feb 09 15:26:58 2011 +0900 @@ -37,19 +37,39 @@ /* This file contains code to handle Disconnect Peer messages (DPR and DPA) */ +/* Delay to use before next reconnect attempt */ +int fd_p_dp_newdelay(struct fd_peer * peer) +{ + int delay = peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc; + + switch (peer->p_hdr.info.runtime.pir_lastDC) { + case ACV_DC_REBOOTING: + default: + /* We use TcTimer to attempt reconnection */ + break; + case ACV_DC_BUSY: + /* No need to hammer the overloaded peer */ + delay *= 10; + break; + case ACV_DC_NOT_FRIEND: + /* He does not want to speak to us... let's retry a *lot* later maybe */ + delay *= 200; + break; + } + return delay; +} + /* Handle a received message */ int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer) { TRACE_ENTRY("%p %d %p", msg, req, peer); if (req) { - /* We received a DPR, save the Disconnect-Cause and terminate the connection */ + /* We received a DPR, save the Disconnect-Cause and go to CLOSING_GRACE or terminate the connection */ struct avp * dc; - int delay = peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc; CHECK_FCT( fd_msg_search_avp ( *msg, fd_dict_avp_DC, &dc )); if (dc) { - /* Check the value is consistent with the saved one */ struct avp_hdr * hdr; CHECK_FCT( fd_msg_avp_hdr( dc, &hdr ) ); if (hdr->avp_value == NULL) { @@ -59,30 +79,20 @@ ASSERT(0); /* To check if this really happens, and understand why... */ } + /* save the cause */ peer->p_hdr.info.runtime.pir_lastDC = hdr->avp_value->u32; - - switch (hdr->avp_value->u32) { - case ACV_DC_REBOOTING: - default: - /* We use TcTimer to attempt reconnection */ - break; - case ACV_DC_BUSY: - /* No need to hammer the overloaded peer */ - delay *= 10; - break; - case ACV_DC_NOT_FRIEND: - /* He does not want to speak to us... let's retry a lot later maybe */ - delay *= 200; - break; - } } if (TRACE_BOOL(INFO)) { if (dc) { - struct dict_object * dictobj = NULL; + struct dict_object * dictobj; struct dict_enumval_request er; memset(&er, 0, sizeof(er)); + + /* prepare the request */ CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT ) ); er.search.enum_value.u32 = peer->p_hdr.info.runtime.pir_lastDC; + + /* Search the enum value */ CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, 0 ) ); if (dictobj) { CHECK_FCT( fd_dict_getval( dictobj, &er.search ) ); @@ -99,30 +109,43 @@ CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) ); CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 1 ) ); - /* Move to CLOSING state to failover outgoing messages (and avoid failing the DPA...) */ + /* Move to CLOSING state to failover outgoing messages (and avoid failing over the DPA...) */ CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) ); /* Now send the DPA */ CHECK_FCT( fd_out_send( msg, NULL, peer, FD_CNX_ORDERED) ); - /* Move to CLOSED state */ - fd_psm_cleanup(peer, 0); - - /* Reset the timer for next connection attempt -- we'll retry sooner or later depending on the disconnection cause */ - fd_psm_next_timeout(peer, 1, delay); - + if (fd_cnx_isMultichan(peer->p_cnxctx)) { + /* There is a possibililty that messages are still in the pipe coming here, so let's grace for 1 second */ + CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING_GRACE) ); + fd_psm_next_timeout(peer, 0, 1); + + } else { + /* Move to CLOSED state */ + fd_psm_cleanup(peer, 0); + + /* Reset the timer for next connection attempt -- we'll retry sooner or later depending on the disconnection cause */ + fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer)); + } } else { /* We received a DPA */ - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state != STATE_CLOSING) { - TRACE_DEBUG(INFO, "Ignoring DPA received in state %s", STATE_STR(peer->p_hdr.info.runtime.pir_state)); + int curstate = fd_peer_getstate(peer); + if (curstate != STATE_CLOSING) { + TRACE_DEBUG(INFO, "Ignoring DPA received in state %s", STATE_STR(curstate)); } /* In theory, we should control the Result-Code AVP. But since we will not go back to OPEN state here anyway, let's skip it */ + + /* TODO("Control Result-Code in the DPA") */ CHECK_FCT_DO( fd_msg_free( *msg ), /* continue */ ); *msg = NULL; - /* The calling function handles cleaning the PSM and terminating the peer since we return in CLOSING state */ + if (fd_cnx_isMultichan(peer->p_cnxctx)) { + CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING_GRACE) ); + fd_psm_next_timeout(peer, 0, 1); + peer->p_flags.pf_localterm = 1; + } + /* otherwise, return in CLOSING state, the psm will handle it */ } return 0; @@ -152,7 +175,7 @@ memset(&er, 0, sizeof(er)); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT ) ); er.search.enum_name = reason ?: "REBOOTING"; - CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, ENOENT ) ); + CHECK_FCT_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, ENOENT ), { ASSERT(0); /* internal error: unknown reason */ } ); CHECK_FCT( fd_dict_getval( dictobj, &er.search ) ); /* Set the value in the AVP */
--- a/libfdcore/p_dw.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/p_dw.c Wed Feb 09 15:26:58 2011 +0900 @@ -37,16 +37,17 @@ /* This file contains code to handle Device Watchdog messages (DWR and DWA) */ -/* Check the value of Origin-State-Id is consistent in a DWR or DWA -- we just log if it is not the case */ -static void check_state_id(struct msg * msg, struct fd_peer * peer) +/* Check the value of Origin-State-Id is consistent in a DWR or DWA -- we return an error otherwise */ +static int check_state_id(struct msg * msg, struct fd_peer * peer) { struct avp * osi; + /* Check if the request contains the Origin-State-Id */ - CHECK_FCT_DO( fd_msg_search_avp ( msg, fd_dict_avp_OSI, &osi ), return ); + CHECK_FCT( fd_msg_search_avp ( msg, fd_dict_avp_OSI, &osi ) ); if (osi) { /* Check the value is consistent with the saved one */ struct avp_hdr * hdr; - CHECK_FCT_DO( fd_msg_avp_hdr( osi, &hdr ), return ); + CHECK_FCT( fd_msg_avp_hdr( osi, &hdr ) ); if (hdr->avp_value == NULL) { /* This is a sanity check */ TRACE_DEBUG(NONE, "BUG: Unset value in Origin-State-Id in DWR / DWA"); @@ -55,12 +56,14 @@ } if (peer->p_hdr.info.runtime.pir_orstate != hdr->avp_value->u32) { - fd_log_debug("Received a new Origin-State-Id from peer %s! (%x / %x)\n", + TRACE_DEBUG(INFO, "Received a new Origin-State-Id from peer '%s'! (%x -> %x); resetting the connection.\n", peer->p_hdr.info.pi_diamid, - hdr->avp_value->u32, - peer->p_hdr.info.runtime.pir_orstate ); + peer->p_hdr.info.runtime.pir_orstate, + hdr->avp_value->u32 ); + return EINVAL; } } + return 0; } /* Create and send a DWR */ @@ -91,7 +94,7 @@ TRACE_ENTRY("%p %d %p", msg, req, peer); /* Check the value of OSI for information */ - check_state_id(*msg, peer); + CHECK_FCT( check_state_id(*msg, peer) ); if (req) { /* If we receive a DWR, send back a DWA */ @@ -101,7 +104,7 @@ CHECK_FCT( fd_out_send( msg, peer->p_cnxctx, peer, FD_CNX_ORDERED) ); } else { - /* Just discard the DWA */ + /* Discard the DWA */ CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ ); *msg = NULL; @@ -122,8 +125,7 @@ } /* If we are in REOPEN state, increment the counter */ - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state == STATE_REOPEN) { + if (fd_peer_getstate(peer) == STATE_REOPEN) { peer->p_flags.pf_reopen_cnt += 1; if (peer->p_flags.pf_reopen_cnt) {
--- a/libfdcore/p_expiry.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/p_expiry.c Wed Feb 09 15:26:58 2011 +0900 @@ -58,10 +58,9 @@ CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), goto error ); for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { - struct fd_peer * peer = (struct fd_peer *)li; + struct fd_peer * peer = (struct fd_peer *)li->o; - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) + if (fd_peer_getstate(peer) != STATE_ZOMBIE) continue; if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_ALWAYS) @@ -77,7 +76,7 @@ /* Now delete peers that are in the purge list */ while (!FD_IS_LIST_EMPTY(&purge)) { - struct fd_peer * peer = (struct fd_peer *)(purge.next); + struct fd_peer * peer = (struct fd_peer *)(purge.next->o); fd_list_unlink(&peer->p_hdr.chain); TRACE_DEBUG(INFO, "Garbage Collect: delete zombie peer '%s'", peer->p_hdr.info.pi_diamid); CHECK_FCT_DO( fd_peer_free(&peer), /* Continue... what else to do ? */ ); @@ -104,7 +103,7 @@ struct timespec now; struct fd_peer * first; - /* Check if there are expiring sessions available */ + /* Check if there are expiring peers available */ if (FD_IS_LIST_EMPTY(&exp_list)) { /* Just wait for a change or cancelation */ CHECK_POSIX_DO( pthread_cond_wait( &exp_cnd, &exp_mtx ), { ASSERT(0); } ); @@ -182,7 +181,7 @@ struct fd_list * li; /* update the p_exp_timer value */ - CHECK_SYS( clock_gettime(CLOCK_REALTIME, &peer->p_exp_timer) ); + CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &peer->p_exp_timer), { ASSERT(0); } ); peer->p_exp_timer.tv_sec += peer->p_hdr.info.config.pic_lft; /* add to the expiry list in appropriate position (probably around the end) */
--- a/libfdcore/p_out.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/p_out.c Wed Feb 09 15:26:58 2011 +0900 @@ -108,7 +108,7 @@ /* Set the thread name */ { char buf[48]; - sprintf(buf, "OUT/%.*s", (int)sizeof(buf) - 5, peer->p_hdr.info.pi_diamid); + snprintf(buf, sizeof(buf), "OUT/%s", peer->p_hdr.info.pi_diamid); fd_log_threadname ( buf ); } @@ -148,8 +148,7 @@ TRACE_ENTRY("%p %p %p %x", msg, cnx, peer, flags); CHECK_PARAMS( msg && *msg && (cnx || (peer && peer->p_cnxctx))); - fd_cpu_flush_cache(); - if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) { + if (fd_peer_getstate(peer) == STATE_OPEN) { /* Normal case: just queue for the out thread to pick it up */ CHECK_FCT( fd_fifo_post(peer->p_tosend, msg) );
--- a/libfdcore/p_psm.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/p_psm.c Wed Feb 09 15:26:58 2011 +0900 @@ -35,6 +35,73 @@ #include "fdcore-internal.h" +/* +This file implement a Peer State Machine which is a mix of: + - the state machine described in rfc3588bis + - the state machine described in rfc3539#section-3.4 + - the following observations. + +The delivery of Diameter messages must not always be unordered: order is important at +begining and end of a connection lifetime. It means we need agility to +switch between "ordering enforced" and "ordering not enforced to counter +HotLB" modes of operation. + +The connection state machine represented in RFC3588 (and rfc3588bis) is +incomplete, because it lacks the SUSPECT state and the 3 DWR/DWA +exchanges (section 5.1) when the peer recovers from this state. +Personnally I don't see the rationale for exchanging 3 messages (why 3?) +but, if we require at least 1 DWR/DWA exchange to be always performed +after the CER/CEA exchange (and initiated by the peer that sent the +CEA), we have a simple way to deal with our ordering problem, as resumed +bellow. Peers are: [i]nitiator, [r]esponder. + (1) [i] SCTP connection attempt. + (2) [r] accept the connection. + (3) [i,r] (if secure port) DTLS handshake, close on failure. + (4) [i] Send CER + (5) [r] Receive CER, send CEA using stream 0, flag "unordered" cleared. + [r] Immediately send a DWR after the CEA, also using stream 0, +flag "unordered" cleared. + [r] Move to STATE_OPEN_NEW state -- equivalent to OPEN except +that all messages are sent ordered at the moment. + (6) [i] receive CEA, move to OPEN state. All messages can be sent +unordered in OPEN state. + [i] As per normal operation, reply with DWA to the DWR. + (7) [r] Upon reception of the DWA, move to OPEN state, messages can be +sent unordered from this point. + +Note about (5) and (6): if the Diameter Identity received in CER or CEA +does not match the credentials from the certificate presented during +DTLS handshake, we may need to specify a path of clean disconnection +(not blocking the remote peer waiting for something). + +This proposed mechanism removes the problem of application messages +received before the CEA by the initiator. Note that if the "old" inband +TLS handshake is used, this handshake plays the same synchronization +role than the new DWR/DWA, which becomes useless. + + +The other time where ordering is important is by the end of connection +lifetime, when one peer is shutting down the link for some reason +(reboot, overload, no activity, etc...). In case of unordered delivery, +we may have: +- peer A sends an application message followed by a DPR. Peer B receives +the DPR first and tears down the connection. Application message is lost. +- Peer B sends an application message, then receives a DPR and answers a +DPA. Peer A receives the DPA before the application message. The +application message is lost. + +This situation is actually quite possible because DPR/DPA messages are +very short, while application messages can be quite large. Therefore, +they require much more time to deliver. + +I really cannot see a way to counter this effect by using the ordering +of the messages, except by applying a timer (state STATE_CLOSING_GRACE). + +However, this problem must be balanced with the fact that the message +that is lost will be in many cases sent again as the failover mechanism +specifies. +*/ + /* The actual declaration of peer_state_str */ DECLARE_STATE_STR(); @@ -100,11 +167,13 @@ peer->p_cb2 = NULL; return 0; } + /* Insert in the active peers list */ CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) ); for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) { struct fd_peer * next_p = (struct fd_peer *)li->o; - int cmp = strcmp(peer->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamid); + int cmp = fd_os_cmp(peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, + next_p->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamidlen); if (cmp < 0) break; } @@ -114,7 +183,7 @@ /* Callback registered when the peer was added, by fd_peer_add */ if (peer->p_cb) { TRACE_DEBUG(FULL, "Calling add callback for peer %s", peer->p_hdr.info.pi_diamid); - (*peer->p_cb)(&peer->p_hdr.info, peer->p_cb_data); + (*peer->p_cb)(&peer->p_hdr.info, peer->p_cb_data); /* TODO: do this in a separate detached thread? */ peer->p_cb = NULL; peer->p_cb_data = NULL; } @@ -177,6 +246,23 @@ } } +/* Read state */ +int fd_peer_get_state(struct peer_hdr *peer) +{ + int ret; + + struct fd_peer * p = (struct fd_peer *)peer; + + if (!CHECK_PEER(p)) + return -1; + + CHECK_POSIX_DO( pthread_mutex_lock(&p->p_state_mtx), return -1 ); + ret = p->p_state; + CHECK_POSIX_DO( pthread_mutex_unlock(&p->p_state_mtx), return -1 ); + + return ret; +} + /* Change state */ int fd_psm_change_state(struct fd_peer * peer, int new_state) @@ -185,8 +271,8 @@ TRACE_ENTRY("%p %d(%s)", peer, new_state, STATE_STR(new_state)); CHECK_PARAMS( CHECK_PEER(peer) ); - fd_cpu_flush_cache(); - old = peer->p_hdr.info.runtime.pir_state; + + old = fd_peer_getstate(peer); if (old == new_state) return 0; @@ -195,8 +281,10 @@ STATE_STR(new_state), peer->p_hdr.info.pi_diamid); - peer->p_hdr.info.runtime.pir_state = new_state; - fd_cpu_flush_cache(); + + CHECK_POSIX( pthread_mutex_lock(&peer->p_state_mtx) ); + peer->p_state = new_state; + CHECK_POSIX( pthread_mutex_unlock(&peer->p_state_mtx) ); if (old == STATE_OPEN) { CHECK_FCT( leave_open_state(peer) ); @@ -254,8 +342,7 @@ void fd_psm_cleanup(struct fd_peer * peer, int terminate) { /* Move to CLOSED state: failover messages, stop OUT thread, unlink peer from active list */ - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) { + if (fd_peer_getstate(peer) != STATE_ZOMBIE) { CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), /* continue */ ); } @@ -284,8 +371,9 @@ { struct fd_peer * peer = (struct fd_peer *)arg; CHECK_PARAMS_DO( CHECK_PEER(peer), return ); - peer->p_hdr.info.runtime.pir_state = STATE_ZOMBIE; - fd_cpu_flush_cache(); + CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), ); + peer->p_state = STATE_ZOMBIE; + CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), ); return; } @@ -297,6 +385,7 @@ int event; size_t ev_sz; void * ev_data; + int cur_state; CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) ); @@ -305,12 +394,14 @@ /* Set the thread name */ { char buf[48]; - sprintf(buf, "PSM/%.*s", (int)sizeof(buf) - 5, peer->p_hdr.info.pi_diamid); + snprintf(buf, sizeof(buf), "PSM/%s", peer->p_hdr.info.pi_diamid); fd_log_threadname ( buf ); } /* The state machine starts in CLOSED state */ - peer->p_hdr.info.runtime.pir_state = STATE_CLOSED; + CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), goto psm_end ); + peer->p_state = STATE_CLOSED; + CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), goto psm_end ); /* Wait that the PSM are authorized to start in the daemon */ CHECK_FCT_DO( fd_psm_waitstart(), goto psm_end ); @@ -325,23 +416,29 @@ psm_loop: /* Get next event */ TRACE_DEBUG(FULL, "'%s' in state '%s' waiting for next event.", - peer->p_hdr.info.pi_diamid, STATE_STR(peer->p_hdr.info.runtime.pir_state)); + peer->p_hdr.info.pi_diamid, STATE_STR(fd_peer_getstate(peer))); CHECK_FCT_DO( fd_event_timedget(peer->p_events, &peer->p_psm_timer, FDEVP_PSM_TIMEOUT, &event, &ev_sz, &ev_data), goto psm_end ); + + cur_state = fd_peer_getstate(peer); + if (cur_state == -1) + goto psm_end; + TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p,%zd)\t'%s'", - STATE_STR(peer->p_hdr.info.runtime.pir_state), + STATE_STR(cur_state), fd_pev_str(event), ev_data, ev_sz, peer->p_hdr.info.pi_diamid); /* Now, the action depends on the current state and the incoming event */ /* The following states are impossible */ - ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_NEW ); - ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE ); - ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_OPEN_HANDSHAKE ); /* because it should exist only between two loops */ + ASSERT( cur_state != STATE_NEW ); + ASSERT( cur_state != STATE_ZOMBIE ); + ASSERT( cur_state != STATE_OPEN_HANDSHAKE ); /* because it should exist only between two loops */ /* Purge invalid events */ if (!CHECK_PEVENT(event)) { TRACE_DEBUG(INFO, "Invalid event received in PSM '%s' : %d", peer->p_hdr.info.pi_diamid, event); + ASSERT(0); /* we should investigate this situation */ goto psm_loop; } @@ -353,15 +450,17 @@ /* Requests to terminate the peer object */ if (event == FDEVP_TERMINATE) { - switch (peer->p_hdr.info.runtime.pir_state) { + switch (cur_state) { case STATE_OPEN: + case STATE_OPEN_NEW: case STATE_REOPEN: - /* We cannot just close the conenction, we have to send a DPR first */ + /* We cannot just close the connection, we have to send a DPR first */ CHECK_FCT_DO( fd_p_dp_initiate(peer, ev_data), goto psm_end ); goto psm_loop; /* case STATE_CLOSING: + case STATE_CLOSING_GRACE: case STATE_WAITCNXACK: case STATE_WAITCNXACK_ELEC: case STATE_WAITCEA: @@ -379,13 +478,6 @@ struct msg * msg = NULL; struct msg_hdr * hdr; - /* If the current state does not allow receiving messages, just drop it */ - if (peer->p_hdr.info.runtime.pir_state == STATE_CLOSED) { - TRACE_DEBUG(FULL, "Purging message in queue while in CLOSED state (%zdb)", ev_sz); - free(ev_data); - goto psm_loop; - } - /* Parse the received buffer */ CHECK_FCT_DO( fd_msg_parse_buffer( (void *)&ev_data, ev_sz, &msg), { @@ -395,8 +487,16 @@ goto psm_loop; } ); + /* If the current state does not allow receiving messages, just drop it */ + if (cur_state == STATE_CLOSED) { + /* In such case, just discard the message */ + fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Purged from peer '%s''s queue (CLOSED state).", peer->p_hdr.info.pi_diamid ); + fd_msg_free(msg); + goto psm_loop; + } + /* Log incoming message */ - fd_msg_log( FD_MSG_LOG_RECEIVED, msg, "Received %zdb from '%s'", ev_sz, peer->p_hdr.info.pi_diamid ); + fd_msg_log( FD_MSG_LOG_RECEIVED, msg, "Received %zdb from '%s' (%s)", ev_sz, peer->p_hdr.info.pi_diamid, STATE_STR(cur_state) ); /* Extract the header */ CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end ); @@ -416,27 +516,34 @@ CHECK_FCT_DO( fd_msg_answ_associate( msg, req ), goto psm_end ); } + if (cur_state == STATE_OPEN_NEW) { + /* OK, we have received something, so the connection is supposedly now in OPEN state at the remote site */ + fd_psm_change_state(peer, STATE_OPEN ); + } + /* Now handle non-link-local messages */ if (fd_msg_is_routable(msg)) { - switch (peer->p_hdr.info.runtime.pir_state) { + switch (cur_state) { /* To maximize compatibility -- should not be a security issue here */ case STATE_REOPEN: case STATE_SUSPECT: case STATE_CLOSING: + case STATE_CLOSING_GRACE: TRACE_DEBUG(FULL, "Accepted a message while not in OPEN state... "); /* The standard situation : */ + case STATE_OPEN_NEW: case STATE_OPEN: /* We received a valid routable message, update the expiry timer */ CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end ); /* Set the message source and add the Route-Record */ - CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, 1, fd_g_config->cnf_dict ), goto psm_end); + CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, 1, fd_g_config->cnf_dict ), goto psm_end); /* Requeue to the global incoming queue */ CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto psm_end ); /* Update the peer timer (only in OPEN state) */ - if ((peer->p_hdr.info.runtime.pir_state == STATE_OPEN) && (!peer->p_flags.pf_dw_pending)) { + if ((cur_state == STATE_OPEN) && (!peer->p_flags.pf_dw_pending)) { fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw); } break; @@ -448,7 +555,7 @@ case STATE_CLOSED: default: /* In such case, just discard the message */ - fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Received from peer '%s' while connection was not in OPEN state.", peer->p_hdr.info.pi_diamid ); + fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Received from peer '%s' while connection was not in state %s.", peer->p_hdr.info.pi_diamid, STATE_STR(cur_state) ); fd_msg_free(msg); } goto psm_loop; @@ -484,8 +591,9 @@ case CC_DISCONNECT_PEER: CHECK_FCT_DO( fd_p_dp_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset ); - if (peer->p_hdr.info.runtime.pir_state == STATE_CLOSING) + if (fd_peer_getstate(peer) == STATE_CLOSING) goto psm_end; + break; case CC_DEVICE_WATCHDOG: @@ -493,7 +601,7 @@ break; default: - /* Unknown / unexpected / invalid message */ + /* Unknown / unexpected / invalid message -- but validated by our dictionary */ TRACE_DEBUG(INFO, "Invalid non-routable command received: %u.", hdr->msg_code); if (hdr->msg_flags & CMD_FLAG_REQUEST) { do { @@ -501,14 +609,14 @@ CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ), break ); /* Set the error code */ - CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_INVALID_HDR_BITS", NULL, NULL, 1 ), break ); + CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_COMMAND_UNSUPPORTED", "Or maybe the P-bit or application Id are erroneous.", NULL, 1 ), break ); /* Send the answer */ CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED), break ); } while (0); } else { /* We did ASK for it ??? */ - fd_log_debug("Invalid PXY flag in answer header ?\n"); + TRACE_DEBUG(INFO, "Received answer with erroneous 'is_routable' result..."); } /* Cleanup the message if not done */ @@ -530,7 +638,7 @@ /* The connection object is broken */ if (event == FDEVP_CNX_ERROR) { - switch (peer->p_hdr.info.runtime.pir_state) { + switch (cur_state) { case STATE_WAITCNXACK_ELEC: /* Abort the initiating side */ fd_p_cnx_abort(peer, 0); @@ -540,6 +648,7 @@ case STATE_WAITCEA: case STATE_OPEN: + case STATE_OPEN_NEW: case STATE_REOPEN: case STATE_WAITCNXACK: case STATE_SUSPECT: @@ -557,6 +666,15 @@ /* We sent a DPR so we are terminating, do not wait for DPA */ goto psm_end; + case STATE_CLOSING_GRACE: + if (peer->p_flags.pf_localterm) /* initiated here */ + goto psm_end; + + fd_psm_cleanup(peer, 0); + + /* Reset the timer for next connection attempt */ + fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer)); + goto psm_loop; } goto psm_loop; } @@ -615,7 +733,7 @@ CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */); peer->p_ini_thr = (pthread_t)NULL; - switch (peer->p_hdr.info.runtime.pir_state) { + switch (cur_state) { case STATE_WAITCNXACK_ELEC: case STATE_WAITCNXACK: fd_p_ce_handle_newcnx(peer, cnx); @@ -623,7 +741,7 @@ default: /* Just abort the attempt and continue */ - TRACE_DEBUG(FULL, "Connection attempt successful but current state is %s, closing...", STATE_STR(peer->p_hdr.info.runtime.pir_state)); + TRACE_DEBUG(FULL, "Connection attempt successful but current state is %s, closing... (too slow?)", STATE_STR(cur_state)); fd_cnx_destroy(cnx); } @@ -637,7 +755,7 @@ CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */); peer->p_ini_thr = (pthread_t)NULL; - switch (peer->p_hdr.info.runtime.pir_state) { + switch (cur_state) { case STATE_WAITCNXACK_ELEC: /* Abort the initiating side */ fd_p_cnx_abort(peer, 0); @@ -652,7 +770,7 @@ default: /* Just ignore */ - TRACE_DEBUG(FULL, "Connection attempt failed but current state is %s, ignoring...", STATE_STR(peer->p_hdr.info.runtime.pir_state)); + TRACE_DEBUG(FULL, "Connection attempt failed but current state is %s, ignoring...", STATE_STR(cur_state)); } goto psm_loop; @@ -660,9 +778,10 @@ /* The timeout for the current state has been reached */ if (event == FDEVP_PSM_TIMEOUT) { - switch (peer->p_hdr.info.runtime.pir_state) { + switch (cur_state) { case STATE_OPEN: case STATE_REOPEN: + case STATE_OPEN_NEW: CHECK_FCT_DO( fd_p_dw_timeout(peer), goto psm_end ); goto psm_loop; @@ -675,7 +794,6 @@ case STATE_SUSPECT: /* Mark the connection problem */ peer->p_flags.pf_cnx_pb = 1; - case STATE_CLOSING: case STATE_WAITCNXACK: case STATE_WAITCEA: @@ -683,6 +801,16 @@ fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc); goto psm_reset; + case STATE_CLOSING_GRACE: + /* The grace period is completed, now close */ + if (peer->p_flags.pf_localterm) + goto psm_end; + + fd_psm_cleanup(peer, 0); + /* Reset the timer for next connection attempt */ + fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer)); + goto psm_loop; + case STATE_WAITCNXACK_ELEC: /* Abort the initiating side */ fd_p_cnx_abort(peer, 0); @@ -696,7 +824,7 @@ } /* Default action : the handling has not yet been implemented. [for debug only] */ - TRACE_DEBUG(INFO, "Missing handler in PSM for '%s'\t<-- '%s'", STATE_STR(peer->p_hdr.info.runtime.pir_state), fd_pev_str(event)); + TRACE_DEBUG(INFO, "Missing handler in PSM for '%s'\t<-- '%s'", STATE_STR(cur_state), fd_pev_str(event)); psm_reset: if (peer->p_flags.pf_delete) goto psm_end; @@ -706,10 +834,9 @@ psm_end: fd_psm_cleanup(peer, 1); TRACE_DEBUG(INFO, "'%s'\t-> STATE_ZOMBIE (terminated)\t'%s'", - STATE_STR(peer->p_hdr.info.runtime.pir_state), + STATE_STR(fd_peer_getstate(peer)), peer->p_hdr.info.pi_diamid); pthread_cleanup_pop(1); /* set STATE_ZOMBIE */ - fd_cpu_flush_cache(); peer->p_psm = (pthread_t)NULL; pthread_detach(pthread_self()); return NULL; @@ -725,7 +852,7 @@ TRACE_ENTRY("%p", peer); /* Check the peer and state are OK */ - CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_hdr.info.runtime.pir_state == STATE_NEW) ); + CHECK_PARAMS( fd_peer_getstate(peer) == STATE_NEW ); /* Create the FIFO for events */ CHECK_FCT( fd_fifo_new(&peer->p_events) ); @@ -743,8 +870,7 @@ TRACE_ENTRY("%p", peer); CHECK_PARAMS( CHECK_PEER(peer) ); - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) { + if (fd_peer_getstate(peer) != STATE_ZOMBIE) { CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, reason) ); } else { TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
--- a/libfdcore/p_sr.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/p_sr.c Wed Feb 09 15:26:58 2011 +0900 @@ -35,10 +35,6 @@ #include "fdcore-internal.h" -#ifndef SR_DEBUG_LVL -#define SR_DEBUG_LVL ANNOYING -#endif /* SR_DEBUG_LVL */ - /* Structure to store a sent request */ struct sentreq { struct fd_list chain; /* the "o" field points directly to the hop-by-hop of the request (uint32_t *) */ @@ -68,17 +64,23 @@ { struct fd_list * li; struct timespec now; - if (!TRACE_BOOL(SR_DEBUG_LVL)) + + if (!TRACE_BOOL(ANNOYING)) return; + + fd_log_debug("%sSentReq list @%p:\n", text, srlist); + CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), ); - fd_log_debug("%sSentReq list @%p:\n", text, srlist); + for (li = srlist->next; li != srlist; li = li->next) { struct sentreq * sr = (struct sentreq *)li; uint32_t * nexthbh = li->o; - fd_log_debug(" - Next req (%x): [since %ld.%06ld sec]\n", *nexthbh, + + fd_log_debug(" - Next req (hbh:%x): [since %ld.%06ld sec]\n", *nexthbh, (now.tv_nsec >= sr->added_on.tv_nsec) ? (now.tv_sec - sr->added_on.tv_sec) : (now.tv_sec - sr->added_on.tv_sec - 1), (now.tv_nsec >= sr->added_on.tv_nsec) ? (now.tv_nsec - sr->added_on.tv_nsec) / 1000 : (now.tv_nsec - sr->added_on.tv_nsec + 1000000000) / 1000); - fd_msg_dump_one(SR_DEBUG_LVL + 1, sr->req); + + fd_msg_dump_one(ANNOYING + 1, sr->req); } } @@ -116,7 +118,7 @@ return NULL; } -/* thread that handles messages expiring. The thread is started / cancelled only when needed */ +/* thread that handles messages expiring. The thread is started only when needed */ static void * sr_expiry_th(void * arg) { struct sr_list * srlist = arg; struct msg * expired_req; @@ -128,7 +130,7 @@ /* Set the thread name */ { char buf[48]; - sprintf(buf, "ReqExp/%.*s", (int)sizeof(buf) - 8, ((struct fd_peer *)(srlist->exp.o))->p_hdr.info.pi_diamid); + snprintf(buf, sizeof(buf), "ReqExp/%s", ((struct fd_peer *)(srlist->exp.o))->p_hdr.info.pi_diamid); fd_log_threadname ( buf ); } @@ -312,7 +314,7 @@ }); } else { /* Just free the request. */ - fd_msg_log( FD_MSG_LOG_DROPPED, sr->req, "Local message discarded during failover" ); + fd_msg_log( FD_MSG_LOG_DROPPED, sr->req, "Sent & unanswered local message discarded during failover." ); CHECK_FCT_DO(fd_msg_free(sr->req), /* Ignore */); } free(sr);
--- a/libfdcore/peers.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/peers.c Wed Feb 09 15:26:58 2011 +0900 @@ -72,6 +72,8 @@ fd_list_init(&p->p_hdr.info.runtime.pir_apps, p); p->p_eyec = EYEC_PEER; + CHECK_POSIX( pthread_mutex_init(&p->p_state_mtx, NULL) ); + fd_list_init(&p->p_actives, p); fd_list_init(&p->p_expiry, p); CHECK_FCT( fd_fifo_new(&p->p_tosend) ); @@ -93,22 +95,32 @@ struct fd_peer *p = NULL; struct fd_list * li; int ret = 0; + TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data); CHECK_PARAMS(info && info->pi_diamid); + if (info->config.pic_realm) { + if (!fd_os_is_valid_DiameterIdentity((os0_t)info->config.pic_realm, strlen(info->config.pic_realm))) { + TRACE_DEBUG(INFO, "'%s' is not a valid DiameterIdentity.", info->config.pic_realm); + return EINVAL; + } + } + /* Create a structure to contain the new peer information */ CHECK_FCT( fd_peer_alloc(&p) ); /* Copy the informations from the parameters received */ - CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) ); + p->p_hdr.info.pi_diamid = info->pi_diamid; + CHECK_FCT( fd_os_validate_DiameterIdentity(&p->p_hdr.info.pi_diamid, &p->p_hdr.info.pi_diamidlen, 1) ); memcpy( &p->p_hdr.info.config, &info->config, sizeof(p->p_hdr.info.config) ); + /* Duplicate the strings if provided */ if (info->config.pic_realm) { CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_realm) ); } if (info->config.pic_priority) { - CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_priority) ); + CHECK_MALLOC( p->p_hdr.info.config.pic_priority = strdup(info->config.pic_priority) ); } /* Move the list of endpoints into the peer */ @@ -123,7 +135,7 @@ if (orig_dbg) { CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) ); } else { - CHECK_MALLOC( p->p_dbgorig = strdup("unknown") ); + CHECK_MALLOC( p->p_dbgorig = strdup("unspecified") ); } p->p_cb = cb; p->p_cb_data = cb_data; @@ -133,7 +145,8 @@ for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { struct fd_peer * next = (struct fd_peer *)li; - int cmp = strcasecmp( p->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamid ); + int cmp = fd_os_almostcasecmp( p->p_hdr.info.pi_diamid, p->p_hdr.info.pi_diamidlen, + next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen ); if (cmp > 0) continue; if (cmp == 0) @@ -161,12 +174,11 @@ } /* Search for a peer */ -int fd_peer_getbyid( char * diamid, struct peer_hdr ** peer ) +int fd_peer_getbyid( DiamId_t diamid, size_t diamidlen, int igncase, struct peer_hdr ** peer ) { struct fd_list * li; - - TRACE_ENTRY("%p %p", diamid, peer); - CHECK_PARAMS( diamid && peer ); + TRACE_ENTRY("%p %zd %d %p", diamid, diamidlen, igncase, peer); + CHECK_PARAMS( diamid && diamidlen && peer ); *peer = NULL; @@ -174,7 +186,11 @@ CHECK_POSIX( pthread_rwlock_rdlock(&fd_g_peers_rw) ); for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { struct fd_peer * next = (struct fd_peer *)li; - int cmp = strcasecmp( diamid, next->p_hdr.info.pi_diamid ); + int cmp; + if (igncase) + cmp = fd_os_almostcasecmp( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen ); + else + cmp = fd_os_cmp( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen ); if (cmp > 0) continue; if (cmp == 0) @@ -254,6 +270,7 @@ fd_list_unlink(&p->p_actives); CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ ); + CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_state_mtx), /* continue */); CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */); CHECK_POSIX_DO( pthread_cond_destroy(&p->p_sr.cnd), /* continue */); @@ -282,10 +299,9 @@ CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ ); for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { - struct fd_peer * peer = (struct fd_peer *)li; + struct fd_peer * peer = (struct fd_peer *)li->o; - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) { + if (fd_peer_getstate(peer) != STATE_ZOMBIE) { CHECK_FCT_DO( fd_psm_terminate(peer, "REBOOTING"), /* continue */ ); } else { li = li->prev; /* to avoid breaking the loop */ @@ -306,14 +322,13 @@ while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) { /* Allow the PSM(s) to execute */ - sched_yield(); + usleep(100000); /* Remove zombie peers */ CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ ); for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { - struct fd_peer * peer = (struct fd_peer *)li; - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state == STATE_ZOMBIE) { + struct fd_peer * peer = (struct fd_peer *)li->o; + if (fd_peer_getstate(peer) == STATE_ZOMBIE) { li = li->prev; /* to avoid breaking the loop */ fd_list_unlink(&peer->p_hdr.chain); fd_list_insert_before(&purge, &peer->p_hdr.chain); @@ -328,7 +343,7 @@ TRACE_DEBUG(INFO, "Forcing connections shutdown"); CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ ); while (!FD_IS_LIST_EMPTY(&fd_g_peers)) { - struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next); + struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next->o); fd_psm_abord(peer); fd_list_unlink(&peer->p_hdr.chain); fd_list_insert_before(&purge, &peer->p_hdr.chain); @@ -338,7 +353,7 @@ /* Free memory objects of all peers */ while (!FD_IS_LIST_EMPTY(&purge)) { - struct fd_peer * peer = (struct fd_peer *)(purge.next); + struct fd_peer * peer = (struct fd_peer *)(purge.next->o); fd_list_unlink(&peer->p_hdr.chain); fd_peer_free(&peer); } @@ -363,9 +378,9 @@ return; } - fd_log_debug("> %s\t%s", STATE_STR(peer->p_hdr.info.runtime.pir_state), peer->p_hdr.info.pi_diamid); + fd_log_debug("> %s\t%s", STATE_STR(fd_peer_getstate(peer)), peer->p_hdr.info.pi_diamid); if (details > INFO) { - fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.runtime.pir_realm ?: "(unknown)"); + fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.runtime.pir_realm ?: "<unknown>"); if (peer->p_hdr.info.runtime.pir_prodname) fd_log_debug("\t['%s' %u]", peer->p_hdr.info.runtime.pir_prodname, peer->p_hdr.info.runtime.pir_firmrev); } @@ -397,19 +412,20 @@ CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ ); for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { - struct fd_peer * np = (struct fd_peer *)li; + struct fd_peer * np = (struct fd_peer *)li->o; fd_peer_dump(np, details); } CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ ); } +static struct dict_object *avp_oh_model = NULL; +static pthread_mutex_t cache_avp_lock = PTHREAD_MUTEX_INITIALIZER; + /* Handle an incoming CER request on a new connection */ int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx ) { struct msg * msg; - struct dict_object *avp_oh_model; - avp_code_t code = AC_ORIGIN_HOST; struct avp *avp_oh; struct avp_hdr * avp_hdr; struct fd_list * li; @@ -423,20 +439,40 @@ msg = *cer; + /* If needed, resolve the dictioanry model for Origin-Host */ + CHECK_POSIX( pthread_mutex_lock(&cache_avp_lock) ); + if (!avp_oh_model) { + avp_code_t code = AC_ORIGIN_HOST; + int ret; + CHECK_FCT_DO( ret = fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT), + { CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) ); return ret; } ); + } + CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) ); + /* Find the Diameter Identity of the remote peer in the message */ - CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT) ); CHECK_FCT( fd_msg_search_avp ( msg, avp_oh_model, &avp_oh ) ); + ASSERT(avp_oh); /* otherwise it should not have passed rules validation, right? */ CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) ); + /* First, check if the Origin-Host value */ + if (!fd_os_is_valid_DiameterIdentity(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len)) { + TRACE_DEBUG(INFO, "Received new CER with invalid \\0 in its Origin-Host"); + CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ) ); + CHECK_FCT( fd_msg_rescode_set(*cer, "ER_DIAMETER_INVALID_AVP_VALUE", + "Your Origin-Host contains invalid characters.", avp_oh, 1 ) ); + CHECK_FCT( fd_out_send(cer, *cnx, NULL, FD_CNX_ORDERED) ); + return EINVAL; + } + /* Search if we already have this peer id in our list. We take directly the write lock so that we don't need to upgrade if it is a new peer. * There is space for a small optimization here if needed. */ CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) ); for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) { - peer = (struct fd_peer *)li; - /* It is probably unwise to use strcasecmp on UTF8 data... To be improved! */ - int cmp = strncasecmp( (char *)avp_hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, avp_hdr->avp_value->os.len ); + int cmp; + peer = (struct fd_peer *)li->o; + cmp = fd_os_almostcasecmp( avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen ); if (cmp > 0) continue; if (cmp == 0) @@ -450,9 +486,9 @@ CHECK_FCT_DO( ret = fd_peer_alloc(&peer), goto out ); /* Set the peer Diameter Id and the responder flag parameters */ - CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = malloc(avp_hdr->avp_value->os.len + 1), { ret = ENOMEM; goto out; } ); - memcpy(peer->p_hdr.info.pi_diamid, avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len); - peer->p_hdr.info.pi_diamid[avp_hdr->avp_value->os.len] = '\0'; + CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = os0dup(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len), + { ret = ENOMEM; goto out; } ); + peer->p_hdr.info.pi_diamidlen = avp_hdr->avp_value->os.len; CHECK_MALLOC_DO( peer->p_dbgorig = strdup(fd_cnx_getid(*cnx)), { ret = ENOMEM; goto out; } ); peer->p_flags.pf_responder = 1; peer->p_flags.pf_delete = 1; @@ -469,16 +505,18 @@ CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out ); } else { /* Check if the peer is in zombie state */ - fd_cpu_flush_cache(); - if (peer->p_hdr.info.runtime.pir_state == STATE_ZOMBIE) { + if (fd_peer_getstate(peer) == STATE_ZOMBIE) { /* Re-activate the peer */ if (peer->p_hdr.info.config.pic_flags.exp) peer->p_flags.pf_responder = 1; - peer->p_hdr.info.runtime.pir_state = STATE_NEW; + CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), ); + peer->p_state = STATE_NEW; + CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), ); + peer->p_flags.pf_localterm = 0; CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out ); } } - + /* Send the new connection event to the PSM */ CHECK_MALLOC_DO( ev_data = malloc(sizeof(struct cnx_incoming)), { ret = ENOMEM; goto out; } ); memset(ev_data, 0, sizeof(ev_data));
--- a/libfdcore/routing_dispatch.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/routing_dispatch.c Wed Feb 09 15:26:58 2011 +0900 @@ -207,8 +207,8 @@ struct rtd_candidate *c = (struct rtd_candidate *) li; struct fd_peer * peer; struct fd_app *found; - CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) ); - if (peer && (peer->p_hdr.info.runtime.pir_relay == 0)) { + CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) ); + if (peer && !peer->p_hdr.info.runtime.pir_relay) { /* Check if the remote peer advertised the message's appli */ CHECK_FCT( fd_app_check(&peer->p_hdr.info.runtime.pir_apps, hdr->msg_appl, &found) ); if (!found) @@ -263,23 +263,23 @@ /* Now, check each candidate against these AVP values */ for (li = candidates->next; li != candidates; li = li->next) { struct rtd_candidate *c = (struct rtd_candidate *) li; + + #if 0 /* this is actually useless since the sending process will also ensure that the peer is still available */ struct fd_peer * peer; - CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) ); - if (peer) { - if (dh - && (dh->os.len == strlen(peer->p_hdr.info.pi_diamid)) - /* Here again we use strncasecmp on UTF8 data... This should probably be changed. */ - && (strncasecmp(peer->p_hdr.info.pi_diamid, (char *)dh->os.data, dh->os.len) == 0)) { - /* The candidate is the Destination-Host */ - c->score += FD_SCORE_FINALDEST; - } else { - if (dr && peer->p_hdr.info.runtime.pir_realm - && (dr->os.len == strlen(peer->p_hdr.info.runtime.pir_realm)) - /* Yet another case where we use strncasecmp on UTF8 data... Hmmm :-( */ - && (strncasecmp(peer->p_hdr.info.runtime.pir_realm, (char *)dr->os.data, dr->os.len) == 0)) { - /* The candidate's realm matchs the Destination-Realm */ - c->score += FD_SCORE_REALM; - } + /* Since the candidates list comes from the peers list, we do not have any issue with upper/lower case to find the peer object */ + CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) ); + if (!peer) + continue; /* it has been deleted since the candidate list was generated; avoid sending to this one in that case. */ + #endif /* 0 */ + + /* In the AVPs, the value comes from the network, so let's be case permissive */ + if (dh && !fd_os_almostcasecmp(dh->os.data, dh->os.len, c->diamid, c->diamidlen) ) { + /* The candidate is the Destination-Host */ + c->score += FD_SCORE_FINALDEST; + } else { + if (dr && !fd_os_almostcasecmp(dr->os.data, dr->os.len, c->realm, c->realmlen) ) { + /* The candidate's realm matchs the Destination-Realm */ + c->score += FD_SCORE_REALM; } } } @@ -297,8 +297,10 @@ int i; TRACE_ENTRY("%p %p %p", un, excl_idx, at_idx); - CHECK_PARAMS_DO( un && excl_idx, return ); + CHECK_PARAMS_DO( un && excl_idx && at_idx, return ); + *excl_idx = 0; + *at_idx = 0; /* Search if there is a '!' before any '@' -- do we need to check it contains a '.' ? */ for (i = 0; i < un->os.len; i++) { @@ -306,67 +308,47 @@ if ( un->os.data[i] == (unsigned char) '!' ) { if (!*excl_idx) *excl_idx = i; - if (!at_idx) - return; + continue; } /* If we reach the realm part, we can stop */ if ( un->os.data[i] == (unsigned char) '@' ) { - if (at_idx) - *at_idx = i; + *at_idx = i; break; } + /* Stop if we find a \0 in the middle */ + if ( un->os.data[i] == 0 ) { + return; + } /* Skip escaped characters */ if ( un->os.data[i] == (unsigned char) '\\' ) { i++; continue; } - /* Skip UTF-8 characters spanning on several bytes */ - if ( (un->os.data[i] & 0xF8) == 0xF0 ) { /* 11110zzz */ - i += 3; - continue; - } - if ( (un->os.data[i] & 0xF0) == 0xE0 ) { /* 1110yyyy */ - i += 2; - continue; - } - if ( (un->os.data[i] & 0xE0) == 0xC0 ) { /* 110yyyxx */ - i += 1; - continue; - } } return; } /* Test if a User-Name AVP contains a Decorated NAI -- RFC4282, RFC5729 */ -static int is_decorated_NAI(union avp_value * un) +/* Create new User-Name and Destination-Realm values */ +static int process_decorated_NAI(int * was_nai, union avp_value * un, union avp_value * dr) { - int i; - TRACE_ENTRY("%p", un); - - /* If there was no User-Name, we return false */ - if (un == NULL) - return 0; - - nai_get_indexes(un, &i, NULL); - - return i; -} - -/* Create new User-Name and Destination-Realm values */ -static int process_decorated_NAI(union avp_value * un, union avp_value * dr) -{ - int at_idx = 0, sep_idx = 0; + int at_idx, sep_idx; unsigned char * old_un; - TRACE_ENTRY("%p %p", un, dr); - CHECK_PARAMS(un && dr); + TRACE_ENTRY("%p %p %p", was_nai, un, dr); + CHECK_PARAMS(was_nai && un && dr); /* Save the decorated User-Name, for example 'homerealm.example.net!user@otherrealm.example.net' */ old_un = un->os.data; /* Search the positions of the first '!' and the '@' in the string */ nai_get_indexes(un, &sep_idx, &at_idx); - CHECK_PARAMS( (0 < sep_idx) && (sep_idx < at_idx) && (at_idx < un->os.len)); + if ((!sep_idx) || (sep_idx > at_idx) || !fd_os_is_valid_DiameterIdentity(old_un, sep_idx /* this is the new realm part */)) { + *was_nai = 0; + return 0; + } + + *was_nai = 1; /* Create the new User-Name value */ CHECK_MALLOC( un->os.data = malloc( at_idx ) ); @@ -389,6 +371,7 @@ return 0; } + /* Function to return an error to an incoming request */ static int return_error(struct msg ** pmsg, char * error_code, char * error_message, struct avp * failedavp) { @@ -397,15 +380,16 @@ /* Get the source of the message */ { - char * id; - CHECK_FCT( fd_msg_source_get( *pmsg, &id ) ); + DiamId_t id; + size_t idlen; + CHECK_FCT( fd_msg_source_get( *pmsg, &id, &idlen ) ); if (id == NULL) { is_loc = 1; /* The message was issued locally */ } else { /* Search the peer with this id */ - CHECK_FCT( fd_peer_getbyid( id, (void *)&peer ) ); + CHECK_FCT( fd_peer_getbyid( id, idlen, 0, (void *)&peer ) ); if (!peer) { fd_msg_log(FD_MSG_LOG_DROPPED, *pmsg, "Unable to send error '%s' to deleted peer '%s' in reply to this message.", error_code, id); @@ -438,7 +422,6 @@ /* Second part : threads moving messages in the daemon */ /****************************************************************************/ -/* These are the functions of each threads: dispatch & routing */ /* The DISPATCH message processing */ static int msg_dispatch(struct msg ** pmsg) { @@ -446,8 +429,8 @@ int is_req = 0, ret; struct session * sess; enum disp_action action; - const char * ec = NULL; - const char * em = NULL; + char * ec = NULL; + char * em = NULL; /* Read the message header */ CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) ); @@ -459,7 +442,7 @@ /* At this point, we need to understand the message content, so parse it */ CHECK_FCT_DO( ret = fd_msg_parse_or_error( pmsg ), { - /* in case of error, the message is already dump'd */ + /* in case of error */ if ((ret == EBADMSG) && (*pmsg != NULL)) { /* msg now contains the answer message to send back */ CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) ); @@ -511,7 +494,7 @@ /* No callback has handled the message, let's reply with a generic error */ em = "The message was not handled by any extension callback"; ec = "DIAMETER_COMMAND_UNSUPPORTED"; - + /* and continue as if an error occurred... */ case DISP_ACT_ERROR: /* We have a problem with delivering the message */ if (ec == NULL) { @@ -527,7 +510,7 @@ /* Create an answer with the error code and message */ CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, 0 ) ); - CHECK_FCT( fd_msg_rescode_set(*pmsg, (char *)ec, (char *)em, NULL, 1 ) ); + CHECK_FCT( fd_msg_rescode_set(*pmsg, ec, em, NULL, 1 ) ); case DISP_ACT_SEND: /* Now, send the message */ @@ -544,7 +527,8 @@ struct msg_hdr * hdr; int is_req = 0; int is_err = 0; - char * qry_src = NULL; + DiamId_t qry_src = NULL; + size_t qry_src_len = 0; /* Read the message header */ CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) ); @@ -571,7 +555,8 @@ /* Check if we have local support for the message application */ if ( (hdr->msg_appl == 0) || (hdr->msg_appl == AI_RELAY) ) { - TRACE_DEBUG(INFO, "Received a routable message with application id 0, returning DIAMETER_APPLICATION_UNSUPPORTED"); + TRACE_DEBUG(INFO, "Received a routable message with application id 0 or " _stringize(AI_RELAY) " (relay),\n" + " returning DIAMETER_APPLICATION_UNSUPPORTED"); CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", "Routable message with application id 0 or relay", NULL) ); return 0; } else { @@ -586,6 +571,9 @@ struct avp_hdr * ahdr; struct fd_pei error_info; int ret; + + memset(&error_info, 0, sizeof(struct fd_pei)); + CHECK_FCT( fd_msg_avp_hdr( avp, &ahdr ) ); if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) { @@ -603,11 +591,10 @@ } ); ASSERT( ahdr->avp_value ); /* Compare the Destination-Host AVP of the message with our identity */ - if (ahdr->avp_value->os.len != fd_g_config->cnf_diamid_len) { - is_dest_host = NO; + if (!fd_os_almostcasecmp(ahdr->avp_value->os.data, ahdr->avp_value->os.len, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len)) { + is_dest_host = YES; } else { - is_dest_host = (strncasecmp(fd_g_config->cnf_diamid, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamid_len) - ? NO : YES); + is_dest_host = NO; } break; @@ -625,14 +612,14 @@ ASSERT( ahdr->avp_value ); dr_val = ahdr->avp_value; /* Compare the Destination-Realm AVP of the message with our identity */ - if (ahdr->avp_value->os.len != fd_g_config->cnf_diamrlm_len) { - is_dest_realm = NO; + if (!fd_os_almostcasecmp(dr_val->os.data, dr_val->os.len, fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len)) { + is_dest_realm = YES; } else { - is_dest_realm = (strncasecmp(fd_g_config->cnf_diamrlm, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamrlm_len) - ? NO : YES); + is_dest_realm = NO; } break; + /* we also use User-Name for decorated NAI */ case AC_USER_NAME: /* Parse this AVP */ CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ), @@ -651,6 +638,7 @@ } } + /* Stop when we found all 3 AVPs -- they are supposed to be at the beginning of the message, so this should be fast */ if ((is_dest_host != UNKNOWN) && (is_dest_realm != UNKNOWN) && un) break; @@ -681,22 +669,23 @@ /* If the message is explicitely for someone else */ if ((is_dest_host == NO) || (is_dest_realm == NO)) { if (fd_g_config->cnf_flags.no_fwd) { - CHECK_FCT( return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "This peer is not an agent", NULL) ); + CHECK_FCT( return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "I am not a Diameter agent", NULL) ); return 0; } } else { /* Destination-Host was not set, and Destination-Realm is matching : we may handle or pass to a fellow peer */ + int is_nai = 0; /* test for decorated NAI (RFC5729 section 4.4) */ - if (is_decorated_NAI(un_val)) { - /* Handle the decorated NAI */ - CHECK_FCT_DO( process_decorated_NAI(un_val, dr_val), - { - /* If the process failed, we assume it is because of the AVP format */ - CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un) ); - return 0; - } ); - + /* Handle the decorated NAI */ + CHECK_FCT_DO( process_decorated_NAI(&is_nai, un_val, dr_val), + { + /* If the process failed, we assume it is because of the AVP format */ + CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un) ); + return 0; + } ); + + if (is_nai) { /* We have transformed the AVP, now submit it again in the queue */ CHECK_FCT(fd_fifo_post(fd_g_incoming, pmsg) ); return 0; @@ -723,7 +712,7 @@ /* Retrieve the corresponding query and its origin */ CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) ); - CHECK_FCT( fd_msg_source_get( qry, &qry_src ) ); + CHECK_FCT( fd_msg_source_get( qry, &qry_src, &qry_src_len ) ); if ((!qry_src) && (!is_err)) { /* The message is a normal answer to a request issued localy, we do not call the callbacks chain on it. */ @@ -732,6 +721,7 @@ } /* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */ + TODO("Callback for answers with a Redirect code?"); } /* Call all registered callbacks for this message */ @@ -799,20 +789,20 @@ /* For answers, the routing is very easy */ if ( ! is_req ) { struct msg * qry; - char * qry_src = NULL; + DiamId_t qry_src = NULL; + size_t qry_src_len = 0; struct msg_hdr * qry_hdr; struct fd_peer * peer = NULL; /* Retrieve the corresponding query and its origin */ CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) ); - CHECK_FCT( fd_msg_source_get( qry, &qry_src ) ); + CHECK_FCT( fd_msg_source_get( qry, &qry_src, &qry_src_len ) ); ASSERT( qry_src ); /* if it is NULL, the message should have been in the LOCAL queue! */ /* Find the peer corresponding to this name */ - CHECK_FCT( fd_peer_getbyid( qry_src, (void *) &peer ) ); - fd_cpu_flush_cache(); - if ((!peer) || (peer->p_hdr.info.runtime.pir_state != STATE_OPEN)) { + CHECK_FCT( fd_peer_getbyid( qry_src, qry_src_len, 0, (void *) &peer ) ); + if (fd_peer_getstate(peer) != STATE_OPEN) { fd_msg_log( FD_MSG_LOG_DROPPED, *pmsg, "Unable to forward answer to deleted / closed peer '%s'.", qry_src); fd_msg_free(*pmsg); *pmsg = NULL; @@ -843,7 +833,12 @@ CHECK_FCT( pthread_rwlock_rdlock(&fd_g_activ_peers_rw) ); for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) { struct fd_peer * p = (struct fd_peer *)li->o; - CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd, p->p_hdr.info.pi_diamid, p->p_hdr.info.runtime.pir_realm), { CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } ); + CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd, + p->p_hdr.info.pi_diamid, + p->p_hdr.info.pi_diamidlen, + p->p_hdr.info.runtime.pir_realm, + p->p_hdr.info.runtime.pir_realmlen), + { CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } ); } CHECK_FCT( pthread_rwlock_unlock(&fd_g_activ_peers_rw) ); @@ -866,8 +861,8 @@ } } ); ASSERT( ahdr->avp_value ); - /* Remove this value from the list */ - fd_rtd_candidate_del(rtd, (char *)ahdr->avp_value->os.data, ahdr->avp_value->os.len); + /* Remove this value from the list. We don't need to pay special attention to the contents here. */ + fd_rtd_candidate_del(rtd, ahdr->avp_value->os.data, ahdr->avp_value->os.len); } /* Go to next AVP */ @@ -875,7 +870,7 @@ } } - /* Note: we reset the scores and pass the message to the callbacks, maybe we could re-use the saved scores when we have received an error ? */ + /* Note: we reset the scores and pass the message to the callbacks, maybe we could re-use the saved scores when we have received an error ? -- TODO */ /* Ok, we have our list in rtd now, let's (re)initialize the scores */ fd_rtd_candidate_extract(rtd, &candidates, FD_SCORE_INI); @@ -927,10 +922,9 @@ break; /* Search for the peer */ - CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) ); + CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) ); - fd_cpu_flush_cache(); - if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) { + if (fd_peer_getstate(peer) == STATE_OPEN) { /* Send to this one */ CHECK_FCT_DO( fd_out_send(pmsg, NULL, peer, 0), continue ); @@ -963,14 +957,15 @@ /* Control of the threads */ static enum { RUN = 0, STOP = 1 } order_val = RUN; -static pthread_mutex_t order_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t order_state_lock = PTHREAD_MUTEX_INITIALIZER; /* Threads report their status */ enum thread_state { NOTRUNNING = 0, RUNNING = 1 }; static void cleanup_state(void * state_loc) { - if (state_loc) - *(enum thread_state *)state_loc = NOTRUNNING; + CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), ); + *(enum thread_state *)state_loc = NOTRUNNING; + CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), ); } /* This is the common thread code (same for routing and dispatching) */ @@ -990,8 +985,9 @@ pthread_cleanup_push( cleanup_state, arg ); /* Mark the thread running */ + CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), ); *(enum thread_state *)arg = RUNNING; - fd_cpu_flush_cache(); + CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), ); do { struct msg * msg; @@ -999,9 +995,9 @@ /* Test the current order */ { int must_stop; - CHECK_POSIX_DO( pthread_mutex_lock(&order_lock), { ASSERT(0); } ); /* we lock to flush the caches */ + CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), { ASSERT(0); } ); /* we lock to flush the caches */ must_stop = (order_val == STOP); - CHECK_POSIX_DO( pthread_mutex_unlock(&order_lock), { ASSERT(0); } ); + CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), { ASSERT(0); } ); if (must_stop) goto end; @@ -1013,7 +1009,15 @@ /* Get the next message from the queue */ { int ret; - ret = fd_fifo_get ( queue, &msg ); + struct timespec ts; + + CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), goto fatal_error ); + ts.tv_sec += 1; + + ret = fd_fifo_timedget ( queue, &msg, &ts ); + if (ret == ETIMEDOUT) + /* loop, check if the thread must stop now */ + continue; if (ret == EPIPE) /* The queue was destroyed, we are probably exiting */ goto end; @@ -1099,15 +1103,18 @@ /* Register the built-in callbacks */ CHECK_FCT( fd_rt_out_register( dont_send_if_no_common_app, NULL, 10, NULL ) ); CHECK_FCT( fd_rt_out_register( score_destination_avp, NULL, 10, NULL ) ); + + TODO("built-in callbacks for Redirect messages?"); + return 0; } /* Ask the thread to terminate after next iteration */ int fd_rtdisp_cleanstop(void) { - CHECK_POSIX( pthread_mutex_lock(&order_lock) ); + CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), ); order_val = STOP; - CHECK_POSIX( pthread_mutex_unlock(&order_lock) ); + CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), ); return 0; } @@ -1116,10 +1123,15 @@ { TRACE_ENTRY("%p %p", st, thr); CHECK_PARAMS_DO(st && thr, return); + int terminated; + + CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), ); + terminated = (*st == NOTRUNNING); + CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), ); + /* Wait for a second for the thread to complete, by monitoring my_state */ - fd_cpu_flush_cache(); - if (*st != NOTRUNNING) { + if (!terminated) { TRACE_DEBUG(INFO, "Waiting for the %s thread to have a chance to terminate", th_name); do { struct timespec ts, ts_final; @@ -1130,8 +1142,11 @@ ts_final.tv_nsec = ts.tv_nsec; while (TS_IS_INFERIOR( &ts, &ts_final )) { - fd_cpu_flush_cache(); - if (*st == NOTRUNNING) + + CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), ); + terminated = (*st == NOTRUNNING); + CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), ); + if (terminated) break; usleep(100000);
--- a/libfdcore/sctp.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/sctp.c Wed Feb 09 15:26:58 2011 +0900 @@ -998,7 +998,7 @@ } /* Send a buffer over a specified stream */ -int fd_sctp_sendstr(int sock, uint16_t strid, uint8_t * buf, size_t len, uint32_t * cc_status) +int fd_sctp_sendstr(struct cnxctx * conn, uint16_t strid, uint8_t * buf, size_t len) { struct msghdr mhdr; struct iovec iov; @@ -1008,8 +1008,8 @@ ssize_t ret; int timedout = 0; - TRACE_ENTRY("%d %hu %p %zd %p", sock, strid, buf, len, cc_status); - CHECK_PARAMS(cc_status); + TRACE_ENTRY("%p %hu %p %zd", conn, strid, buf, len); + CHECK_PARAMS(conn && buf && len); memset(&mhdr, 0, sizeof(mhdr)); memset(&iov, 0, sizeof(iov)); @@ -1036,12 +1036,12 @@ mhdr.msg_control = anci; mhdr.msg_controllen = sizeof(anci); - TRACE_DEBUG(FULL, "Sending %db data on stream %hu of socket %d", len, strid, sock); + TRACE_DEBUG(FULL, "Sending %db data on stream %hu of socket %d", len, strid, conn->cc_socket); again: - ret = sendmsg(sock, &mhdr, 0); + ret = sendmsg(conn->cc_socket, &mhdr, 0); /* Handle special case of timeout */ if ((ret < 0) && (errno == EAGAIN)) { - if (!(*cc_status & CC_STATUS_CLOSING)) + if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) goto again; /* don't care, just ignore */ if (!timedout) { timedout ++; /* allow for one timeout while closing */ @@ -1056,7 +1056,7 @@ } /* Receive the next data from the socket, or next notification */ -int fd_sctp_recvmeta(int sock, uint16_t * strid, uint8_t ** buf, size_t * len, int *event, uint32_t * cc_status) +int fd_sctp_recvmeta(struct cnxctx * conn, uint16_t * strid, uint8_t ** buf, size_t * len, int *event) { ssize_t ret = 0; struct msghdr mhdr; @@ -1067,8 +1067,8 @@ size_t mempagesz = sysconf(_SC_PAGESIZE); /* We alloc buffer by memory pages for efficiency */ int timedout = 0; - TRACE_ENTRY("%d %p %p %p %p %p", sock, strid, buf, len, event, cc_status); - CHECK_PARAMS( (sock > 0) && buf && len && event && cc_status ); + TRACE_ENTRY("%p %p %p %p %p", conn, strid, buf, len, event); + CHECK_PARAMS( conn && buf && len && event ); /* Cleanup out parameters */ *buf = NULL; @@ -1097,12 +1097,12 @@ /* Receive data from the socket */ again: pthread_cleanup_push(free, data); - ret = recvmsg(sock, &mhdr, 0); + ret = recvmsg(conn->cc_socket, &mhdr, 0); pthread_cleanup_pop(0); /* First, handle timeouts (same as fd_cnx_s_recv) */ if ((ret < 0) && (errno == EAGAIN)) { - if (!(*cc_status & CC_STATUS_CLOSING)) + if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) goto again; /* don't care, just ignore */ if (!timedout) { timedout ++; /* allow for one timeout while closing */ @@ -1131,7 +1131,7 @@ if (mhdr.msg_flags & MSG_NOTIFICATION) { union sctp_notification * notif = (union sctp_notification *) data; - TRACE_DEBUG(FULL, "Received %db data of notification on socket %d", datasize, sock); + TRACE_DEBUG(FULL, "Received %db data of notification on socket %d", datasize, conn->cc_socket); switch (notif->sn_header.sn_type) { @@ -1225,9 +1225,9 @@ *strid = sndrcv->sinfo_stream; } - TRACE_DEBUG(FULL, "Received %db data on socket %d, stream %hu", datasize, sock, *strid); + TRACE_DEBUG(FULL, "Received %db data on socket %d, stream %hu", datasize, conn->cc_socket, *strid); } else { - TRACE_DEBUG(FULL, "Received %db data on socket %d (stream ignored)", datasize, sock); + TRACE_DEBUG(FULL, "Received %db data on socket %d (stream ignored)", datasize, conn->cc_socket); } return 0;
--- a/libfdcore/sctps.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/sctps.c Wed Feb 09 15:26:58 2011 +0900 @@ -56,7 +56,7 @@ This complexity is required because we cannot read a socket for a given stream only; we can only get the next message and find its stream. */ - +/* TODO: change this whole wrapper to DTLS which should not require many different threads */ /*************************************************************/ /* threads */ @@ -82,12 +82,11 @@ } ASSERT( conn->cc_proto == IPPROTO_SCTP ); - ASSERT( Target_Queue(conn) ); + ASSERT( fd_cnx_target_queue(conn) ); ASSERT( conn->cc_sctps_data.array ); do { - fd_cpu_flush_cache(); - CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, &strid, &buf, &bufsz, &event, &conn->cc_status), goto fatal ); + CHECK_FCT_DO( fd_sctp_recvmeta(conn, &strid, &buf, &bufsz, &event), goto fatal ); switch (event) { case FDEVP_CNX_MSG_RECV: /* Demux this message to the appropriate fifo, another thread will pull, gnutls process, and send to target queue */ @@ -101,8 +100,7 @@ case FDEVP_CNX_EP_CHANGE: /* Send this event to the target queue */ - fd_cpu_flush_cache(); - CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto fatal ); + CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, bufsz, buf), goto fatal ); break; case FDEVP_CNX_ERROR: @@ -143,7 +141,7 @@ TRACE_ENTRY("%p", arg); CHECK_PARAMS_DO(ctx && ctx->raw_recv && ctx->parent, goto error); cnx = ctx->parent; - ASSERT( Target_Queue(cnx) ); + ASSERT( fd_cnx_target_queue(cnx) ); /* Set the thread name */ { @@ -172,8 +170,7 @@ TRACE_ENTRY("%p %p %zd", tr, data, len); CHECK_PARAMS_DO( tr && data, { errno = EINVAL; return -1; } ); - fd_cpu_flush_cache(); - CHECK_FCT_DO( fd_sctp_sendstr(ctx->parent->cc_socket, ctx->strid, (uint8_t *)data, len, &ctx->parent->cc_status), /* errno is already set */ return -1 ); + CHECK_FCT_DO( fd_sctp_sendstr(ctx->parent, ctx->strid, (uint8_t *)data, len), /* errno is already set */ return -1 ); return len; } @@ -305,15 +302,7 @@ int cmp = 0; struct sr_data * sr = (struct sr_data *)ret; - if ( key.size < sr->key.size ) - break; - - if ( key.size > sr->key.size ) - continue; - - /* Key sizes are equal */ - cmp = memcmp( key.data, sr->key.data, key.size ); - + cmp = fd_os_cmp(key.data, key.size, sr->key.data, sr->key.size); if (cmp > 0) continue; @@ -626,8 +615,7 @@ /* End all TLS sessions, in series (not as efficient as paralel, but simpler) */ for (i = 1; i < conn->cc_sctp_para.pairs; i++) { - fd_cpu_flush_cache(); - if ( ! (conn->cc_status & CC_STATUS_ERROR)) { + if ( ! fd_cnx_teststate(conn, CC_STATUS_ERROR)) { CHECK_GNUTLS_DO( gnutls_bye(conn->cc_sctps_data.array[i].session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) ); } }
--- a/libfdcore/server.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdcore/server.c Wed Feb 09 15:26:58 2011 +0900 @@ -40,6 +40,13 @@ static struct fd_list FD_SERVERS = FD_LIST_INITIALIZER(FD_SERVERS); /* The list of all server objects */ /* We don't need to protect this list, it is only accessed from the main framework thread. */ +enum s_state { + NOT_CREATED=0, + RUNNING, + TERMINATED, + ERROR /* an error occurred, this is not a valid status */ +}; + /* Servers information */ struct server { struct fd_list chain; /* link in the FD_SERVERS list */ @@ -49,12 +56,13 @@ int secur; /* TLS is started immediatly after connection ? */ pthread_t thr; /* The thread listening for new connections */ - int status; /* 0 : not created; 1 : running; 2 : terminated */ + enum s_state state; /* state of the thread */ struct fd_list clients; /* List of clients connected to this server, not yet identified */ pthread_mutex_t clients_mtx; /* Mutex to protect the list of clients */ }; + /* Client information (connecting peer for which we don't have the CER yet) */ struct client { struct fd_list chain; /* link in the server's list of clients */ @@ -64,6 +72,26 @@ }; + +/* Micro functions to read/change the status thread-safely */ +static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER; +static enum s_state get_status(struct server * s) +{ + enum s_state r; + CHECK_POSIX_DO( pthread_mutex_lock(&s_lock), return ERROR ); + r = s->state; + CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return ERROR ); + return r; +} +static void set_status(struct server * s, enum s_state st) +{ + CHECK_POSIX_DO( pthread_mutex_lock(&s_lock), return ); + s->state = st; + CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return ); +} + + + /* Dump all servers information */ void fd_servers_dump() { @@ -72,14 +100,14 @@ fd_log_debug("Dumping servers list :\n"); for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) { struct server * s = (struct server *)li; - fd_cpu_flush_cache(); + enum s_state st = get_status(s); fd_log_debug(" Serv %p '%s': %s, %s, %s\n", s, fd_cnx_getid(s->conn), IPPROTO_NAME( s->proto ), s->secur ? "Secur" : "NotSecur", - (s->status == 0) ? "Thread not created" : - ((s->status == 1) ? "Thread running" : - ((s->status == 2) ? "Thread terminated" : + (st == NOT_CREATED) ? "Thread not created" : + ((st == RUNNING) ? "Thread running" : + ((st == TERMINATED) ? "Thread terminated" : "Thread status unknown"))); /* Dump the client list of this server */ CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), ); @@ -191,8 +219,7 @@ CHECK_PARAMS_DO(s, goto error); fd_log_threadname ( fd_cnx_getid(s->conn) ); - s->status = 1; - fd_cpu_flush_cache(); + set_status(s, RUNNING); /* Accept incoming connections */ CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error ); @@ -224,7 +251,7 @@ error: if (s) - s->status = 2; + set_status(s, TERMINATED); /* Send error signal to the daemon */ TRACE_DEBUG(INFO, "An error occurred in server module! Thread is terminating..."); CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); @@ -265,16 +292,20 @@ #else /* DISABLE_SCTP */ /* Create the server on unsecure port */ - CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) ); - CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) ); - fd_list_insert_before( &FD_SERVERS, &s->chain ); - CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + if (fd_g_config->cnf_port) { + CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) ); + CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) ); + fd_list_insert_before( &FD_SERVERS, &s->chain ); + CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + } /* Create the server on secure port */ - CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 1) ); - CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) ); - fd_list_insert_before( &FD_SERVERS, &s->chain ); - CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + if (fd_g_config->cnf_port_tls) { + CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 1) ); + CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) ); + fd_list_insert_before( &FD_SERVERS, &s->chain ); + CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + } #endif /* DISABLE_SCTP */ } @@ -286,28 +317,37 @@ /* Bind TCP servers on [0.0.0.0] */ if (!fd_g_config->cnf_flags.no_ip4) { - CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) ); - CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET, NULL) ); - fd_list_insert_before( &FD_SERVERS, &s->chain ); - CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + if (fd_g_config->cnf_port) { + CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) ); + CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET, NULL) ); + fd_list_insert_before( &FD_SERVERS, &s->chain ); + CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + } - CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) ); - CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET, NULL) ); - fd_list_insert_before( &FD_SERVERS, &s->chain ); - CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + if (fd_g_config->cnf_port_tls) { + CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) ); + CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET, NULL) ); + fd_list_insert_before( &FD_SERVERS, &s->chain ); + CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + } } + /* Bind TCP servers on [::] */ if (!fd_g_config->cnf_flags.no_ip6) { - - CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) ); - CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET6, NULL) ); - fd_list_insert_before( &FD_SERVERS, &s->chain ); - CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); - CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) ); - CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET6, NULL) ); - fd_list_insert_before( &FD_SERVERS, &s->chain ); - CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + if (fd_g_config->cnf_port) { + CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) ); + CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET6, NULL) ); + fd_list_insert_before( &FD_SERVERS, &s->chain ); + CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + } + + if (fd_g_config->cnf_port_tls) { + CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) ); + CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET6, NULL) ); + fd_list_insert_before( &FD_SERVERS, &s->chain ); + CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + } } } else { /* Create all endpoints -- check flags */ @@ -322,24 +362,30 @@ if (fd_g_config->cnf_flags.no_ip6 && (sa->sa_family == AF_INET6)) continue; - CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) ); - CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, sa->sa_family, ep) ); - fd_list_insert_before( &FD_SERVERS, &s->chain ); - CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + if (fd_g_config->cnf_port) { + CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) ); + CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, sa->sa_family, ep) ); + fd_list_insert_before( &FD_SERVERS, &s->chain ); + CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + } - CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) ); - CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, sa->sa_family, ep) ); - fd_list_insert_before( &FD_SERVERS, &s->chain ); - CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + if (fd_g_config->cnf_port_tls) { + CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) ); + CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, sa->sa_family, ep) ); + fd_list_insert_before( &FD_SERVERS, &s->chain ); + CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + } } } } - /* Now, if we still have not got the list of local adresses, try to read it from the kernel directly */ - if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) { + /* Now, if we had an empty list of local adresses (no address configured), try to read the real addresses from the kernel */ + if (empty_conf_ep) { CHECK_FCT(fd_cnx_get_local_eps(&fd_g_config->cnf_endpoints)); if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) { - TRACE_DEBUG(INFO, "Unable to find the addresses of the local system. Please use \"ListenOn\" parameter in the configuration."); + TRACE_DEBUG(INFO, "Unable to find the address(es) of the local system.\n" + "Please use \"ListenOn\" parameter in the configuration.\n" + "This information is required to generate the CER/CEA messages.\n"); return EINVAL; } }
--- a/libfdproto/CMakeLists.txt Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdproto/CMakeLists.txt Wed Feb 09 15:26:58 2011 +0900 @@ -12,6 +12,7 @@ log.c messages.c msg_log.c + ostr.c rt_data.c sessions.c )
--- a/libfdproto/dictionary.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdproto/dictionary.c Wed Feb 09 15:26:58 2011 +0900 @@ -62,15 +62,17 @@ struct dictionary *dico; /* The dictionary this object belongs to */ union { - struct dict_vendor_data vendor; - struct dict_application_data application; - struct dict_type_data type; - struct dict_enumval_data enumval; - struct dict_avp_data avp; - struct dict_cmd_data cmd; - struct dict_rule_data rule; + struct dict_vendor_data vendor; /* datastr_len = strlen(vendor_name) */ + struct dict_application_data application; /* datastr_len = strlen(application_name) */ + struct dict_type_data type; /* datastr_len = strlen(type_name) */ + struct dict_enumval_data enumval; /* datastr_len = strlen(enum_name) */ + struct dict_avp_data avp; /* datastr_len = strlen(avp_name) */ + struct dict_cmd_data cmd; /* datastr_len = strlen(cmd_name) */ + struct dict_rule_data rule; /* datastr_len = 0 */ } data; /* The data of this object */ + size_t datastr_len; /* cached length of the string inside the data. Saved when the object is created. */ + struct dict_object * parent; /* The parent of this object, if any */ struct fd_list list[NB_LISTS_PER_OBJ];/* used to chain objects.*/ @@ -87,7 +89,7 @@ => VENDORS: list[0]: list of the vendors, ordered by their id. The sentinel is g_dict_vendors (vendor with id 0) list[1]: sentinel for the list of AVPs from this vendor, ordered by AVP code. - list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name. + list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name (fd_os_cmp). => APPLICATIONS: list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0) @@ -96,26 +98,26 @@ => TYPES: list[0]: list of the types, ordered by their names. The sentinel is g_list_types. - list[1]: sentinel for the type_enum list of this type, ordered by their constant name. + list[1]: sentinel for the type_enum list of this type, ordered by their constant name (fd_os_cmp). list[2]: sentinel for the type_enum list of this type, ordered by their constant value. => TYPE_ENUMS: - list[0]: list of the contants for a given type, ordered by the constant name. Sentinel is a (list[1]) element of a TYPE object. + list[0]: list of the contants for a given type, ordered by the constant name (fd_os_cmp). Sentinel is a (list[1]) element of a TYPE object. list[1]: list of the contants for a given type, ordered by the constant value. Sentinel is a (list[2]) element of a TYPE object. list[2]: not used => AVPS: list[0]: list of the AVP from a given vendor, ordered by avp code. Sentinel is a list[1] element of a VENDOR object. - list[1]: list of the AVP from a given vendor, ordered by avp name. Sentinel is a list[2] element of a VENDOR object. + list[1]: list of the AVP from a given vendor, ordered by avp name (fd_os_cmp). Sentinel is a list[2] element of a VENDOR object. list[2]: sentinel for the rule list that apply to this AVP. => COMMANDS: - list[0]: list of the commands, ordered by their names. The sentinel is g_list_cmd_name. + list[0]: list of the commands, ordered by their names (fd_os_cmp). The sentinel is g_list_cmd_name. list[1]: list of the commands, ordered by their command code and 'R' flag. The sentinel is g_list_cmd_code. list[2]: sentinel for the rule list that apply to this command. => RULES: - list[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP name to which they refer. sentinel is list[2] of a command or (grouped) avp. + list[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP vendor & code to which they refer. sentinel is list[2] of a command or (grouped) avp. list[1]: not used list[2]: not used. @@ -222,10 +224,10 @@ /* Functions to manage the objects creation and destruction. */ -/* Duplicate a string inplace */ -#define DUP_string( str ) { \ - char * __str = (str); \ - CHECK_MALLOC( (str) = strdup(__str) ); \ +/* Duplicate a string inplace, save its length */ +#define DUP_string_len( str, plen ) { \ + *(plen) = strlen((str)); \ + str = os0dup( str, *(plen)); \ } /* Initialize an object */ @@ -258,39 +260,39 @@ } /* Initialize the "data" part of an object */ -static int init_object_data(void * dest, void * source, enum dict_object_type type) +static int init_object_data(struct dict_object * dest, void * source, enum dict_object_type type) { TRACE_ENTRY("%p %p %d", dest, source, type); CHECK_PARAMS( dest && source && CHECK_TYPE(type) ); /* Generic: copy the full data structure */ - memcpy( dest, source, dict_obj_info[type].datasize ); + memcpy( &dest->data, source, dict_obj_info[type].datasize ); /* Then strings must be duplicated, not copied */ /* This function might be simplified by always defining the "name" field as the first field of the structures, but... it's error-prone */ switch (type) { case DICT_VENDOR: - DUP_string( ((struct dict_vendor_data *)dest)->vendor_name ); + DUP_string_len( dest->data.vendor.vendor_name, &dest->datastr_len ); break; case DICT_APPLICATION: - DUP_string( ((struct dict_application_data *)dest)->application_name ); + DUP_string_len( dest->data.application.application_name, &dest->datastr_len ); break; case DICT_TYPE: - DUP_string( ((struct dict_type_data *)dest)->type_name ); + DUP_string_len( dest->data.type.type_name, &dest->datastr_len ); break; case DICT_ENUMVAL: - DUP_string( ((struct dict_enumval_data *)dest)->enum_name ); + DUP_string_len( dest->data.enumval.enum_name, &dest->datastr_len ); break; case DICT_AVP: - DUP_string( ((struct dict_avp_data *)dest)->avp_name ); + DUP_string_len( dest->data.avp.avp_name, &dest->datastr_len ); break; case DICT_COMMAND: - DUP_string( ((struct dict_cmd_data *)dest)->cmd_name ); + DUP_string_len( dest->data.cmd.cmd_name, &dest->datastr_len ); break; default: @@ -455,7 +457,7 @@ { TRACE_ENTRY("%p %p", o1, o2); - return strcmp( o1->data.type.type_name, o2->data.type.type_name ); + return fd_os_cmp( o1->data.type.type_name, o1->datastr_len, o2->data.type.type_name, o2->datastr_len ); } /* Compare two type_enum objects by their names (checks already performed) */ @@ -463,25 +465,19 @@ { TRACE_ENTRY("%p %p", o1, o2); - return strcmp( o1->data.enumval.enum_name, o2->data.enumval.enum_name ); + return fd_os_cmp( o1->data.enumval.enum_name, o1->datastr_len, o2->data.enumval.enum_name, o2->datastr_len ); } /* Compare two type_enum objects by their values (checks already performed) */ static int order_enum_by_val ( struct dict_object *o1, struct dict_object *o2 ) { - size_t oslen; - int cmp = 0; - TRACE_ENTRY("%p %p", o1, o2); /* The comparison function depends on the type of data */ switch ( o1->parent->data.type.type_base ) { case AVP_TYPE_OCTETSTRING: - oslen = o1->data.enumval.enum_value.os.len; - if (o2->data.enumval.enum_value.os.len < oslen) - oslen = o2->data.enumval.enum_value.os.len; - cmp = memcmp(o1->data.enumval.enum_value.os.data, o2->data.enumval.enum_value.os.data, oslen ); - return (cmp ? cmp : ORDER_scalar(o1->data.enumval.enum_value.os.len,o2->data.enumval.enum_value.os.len)); + return fd_os_cmp( o1->data.enumval.enum_value.os.data, o1->data.enumval.enum_value.os.len, + o2->data.enumval.enum_value.os.data, o2->data.enumval.enum_value.os.len); case AVP_TYPE_INTEGER32: return ORDER_scalar( o1->data.enumval.enum_value.i32, o2->data.enumval.enum_value.i32 ); @@ -521,7 +517,7 @@ { TRACE_ENTRY("%p %p", o1, o2); - return strcmp( o1->data.avp.avp_name, o2->data.avp.avp_name ); + return fd_os_cmp( o1->data.avp.avp_name, o1->datastr_len, o2->data.avp.avp_name, o2->datastr_len ); } /* Compare two command objects by their names (checks already performed) */ @@ -529,7 +525,7 @@ { TRACE_ENTRY("%p %p", o1, o2); - return strcmp( o1->data.cmd.cmd_name, o2->data.cmd.cmd_name ); + return fd_os_cmp( o1->data.cmd.cmd_name, o1->datastr_len, o2->data.cmd.cmd_name, o2->datastr_len ); } /* Compare two command objects by their codes and flags (request or answer) (checks already performed) */ @@ -554,7 +550,7 @@ } /* Compare two rule object by the AVP vendor & code that they refer (checks already performed) */ -static int order_rule_by_avpn ( struct dict_object *o1, struct dict_object *o2 ) +static int order_rule_by_avpvc ( struct dict_object *o1, struct dict_object *o2 ) { TRACE_ENTRY("%p %p", o1, o2); @@ -589,13 +585,16 @@ } /* For search of strings in lists. isindex= 1 if the string is the ordering key of the list */ -#define SEARCH_string( str, sentinel, datafield, isindex ) { \ - char * __str = (char *) str; \ +/* it is expected that object->datastr_len is the length of the datafield parameter */ +#define SEARCH_os0_l( str, len, sentinel, datafield, isindex ) { \ + char * __str = (char *) (str); \ + size_t __strlen = (size_t)(len); \ int __cmp; \ struct fd_list * __li; \ ret = 0; \ for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \ - __cmp = strcmp(__str, _O(__li->o)->data. datafield ); \ + __cmp = fd_os_cmp(__str, __strlen, \ + _O(__li->o)->data. datafield, _O(__li->o)->datastr_len);\ if (__cmp == 0) { \ if (result) \ *result = _O(__li->o); \ @@ -610,27 +609,28 @@ ret = ENOENT; \ } -/* For search of octetstrings in lists (not \0 terminated). */ -#define SEARCH_ocstring( ostr, length, sentinel, osdatafield, isindex ) { \ - unsigned char * __ostr = (unsigned char *) ostr; \ +/* When len is not provided */ +#define SEARCH_os0( str, sentinel, datafield, isindex ) { \ + char * _str = (char *) (str); \ + size_t _strlen = strlen(_str); \ + SEARCH_os0_l( _str, _strlen, sentinel, datafield, isindex ); \ +} + + +/* For search of octetstrings in lists. */ +#define SEARCH_os( str, strlen, sentinel, osdatafield, isindex ) { \ + uint8_t * __str = (uint8_t *) (str); \ + size_t __strlen = (size_t)(strlen); \ int __cmp; \ - size_t __len; \ struct fd_list * __li; \ ret = 0; \ - for (__li = (sentinel); __li->next != (sentinel); __li = __li->next) { \ - __len = _O(__li->next->o)->data. osdatafield .len; \ - if ( __len > (length) ) \ - __len = (length); \ - __cmp = memcmp(__ostr, \ - _O(__li->next->o)->data. osdatafield .data, \ - __len); \ - if (! __cmp) { \ - __cmp = ORDER_scalar( length, \ - _O(__li->next->o)->data. osdatafield .len); \ - } \ + for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \ + __cmp = fd_os_cmp(__str, __strlen, \ + _O(__li->o)->data. osdatafield .data, \ + _O(__li->o)->data. osdatafield .len); \ if (__cmp == 0) { \ if (result) \ - *result = _O(__li->next->o); \ + *result = _O(__li->o); \ goto end; \ } \ if ((isindex) && (__cmp < 0)) \ @@ -643,17 +643,19 @@ } /* For search of AVP name in rule lists. */ -#define SEARCH_ruleavpname( str, sentinel ) { \ - char * __str = (char *) str; \ +#define SEARCH_ruleavpname( str, strlen, sentinel ) { \ + char * __str = (char *) (str); \ + size_t __strlen = (size_t) (strlen); \ int __cmp; \ struct fd_list * __li; \ ret = 0; \ - for (__li = (sentinel); __li->next != (sentinel); __li = __li->next) { \ - __cmp = strcmp(__str, \ - _O(__li->next->o)->data.rule.rule_avp->data.avp.avp_name);\ + for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \ + __cmp = fd_os_cmp(__str, __strlen, \ + _O(__li->o)->data.rule.rule_avp->data.avp.avp_name, \ + _O(__li->o)->data.rule.rule_avp->datastr_len); \ if (__cmp == 0) { \ if (result) \ - *result = _O(__li->next->o); \ + *result = _O(__li->o); \ goto end; \ } \ if (__cmp < 0) \ @@ -676,11 +678,11 @@ *result = _O(defaultobj); \ goto end; \ } \ - for (__li = (sentinel); __li->next != (sentinel); __li = __li->next) { \ - __cmp= ORDER_scalar(value, _O(__li->next->o)->data. datafield );\ + for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \ + __cmp= ORDER_scalar(value, _O(__li->o)->data. datafield ); \ if (__cmp == 0) { \ if (result) \ - *result = _O(__li->next->o); \ + *result = _O(__li->o); \ goto end; \ } \ if ((isindex) && (__cmp < 0)) \ @@ -697,21 +699,19 @@ int __cmp; \ struct fd_list * __li; \ ret = 0; \ - for ( __li = (sentinel); \ - __li->next != (sentinel); \ - __li = __li->next) { \ + for (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) { \ __cmp = ORDER_scalar(value, \ - _O(__li->next->o)->data.cmd.cmd_code ); \ + _O(__li->o)->data.cmd.cmd_code ); \ if (__cmp == 0) { \ uint8_t __mask, __val; \ - __mask = _O(__li->next->o)->data.cmd.cmd_flag_mask; \ - __val = _O(__li->next->o)->data.cmd.cmd_flag_val; \ + __mask = _O(__li->o)->data.cmd.cmd_flag_mask; \ + __val = _O(__li->o)->data.cmd.cmd_flag_val; \ if ( ! (__mask & CMD_FLAG_REQUEST) ) \ continue; \ if ( ( __val & CMD_FLAG_REQUEST ) != R_flag_val ) \ continue; \ if (result) \ - *result = _O(__li->next->o); \ + *result = _O(__li->o); \ goto end; \ } \ if (__cmp < 0) \ @@ -738,7 +738,7 @@ case VENDOR_BY_NAME: /* "what" is a vendor name */ - SEARCH_string( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0); + SEARCH_os0( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0); break; case VENDOR_OF_APPLICATION: @@ -770,7 +770,7 @@ case APPLICATION_BY_NAME: /* "what" is an application name */ - SEARCH_string( what, &dict->dict_applications.list[0], application.application_name, 0); + SEARCH_os0( what, &dict->dict_applications.list[0], application.application_name, 0); break; case APPLICATION_OF_TYPE: @@ -800,7 +800,7 @@ switch (criteria) { case TYPE_BY_NAME: /* "what" is a type name */ - SEARCH_string( what, &dict->dict_types, type.type_name, 1); + SEARCH_os0( what, &dict->dict_types, type.type_name, 1); break; case TYPE_OF_ENUMVAL: @@ -849,12 +849,12 @@ if ( _what->search.enum_name != NULL ) { /* We are looking for this string */ - SEARCH_string( _what->search.enum_name, &parent->list[1], enumval.enum_name, 1 ); + SEARCH_os0( _what->search.enum_name, &parent->list[1], enumval.enum_name, 1 ); } else { /* We are looking for the value in enum_value */ switch (parent->data.type.type_base) { case AVP_TYPE_OCTETSTRING: - SEARCH_ocstring( _what->search.enum_value.os.data, + SEARCH_os( _what->search.enum_value.os.data, _what->search.enum_value.os.len, &parent->list[2], enumval.enum_value.os , @@ -945,7 +945,7 @@ case AVP_BY_NAME: /* "what" is the AVP name, vendor 0 */ - SEARCH_string( what, &dict->dict_vendors.list[2], avp.avp_name, 1); + SEARCH_os0( what, &dict->dict_vendors.list[2], avp.avp_name, 1); break; case AVP_BY_CODE_AND_VENDOR: @@ -968,10 +968,10 @@ /* We now have our vendor = head of the appropriate avp list */ if (criteria == AVP_BY_NAME_AND_VENDOR) { - SEARCH_string( _what->avp_name, &vendor->list[2], avp.avp_name, 1); + SEARCH_os0( _what->avp_name, &vendor->list[2], avp.avp_name, 1); } else { /* AVP_BY_CODE_AND_VENDOR */ - SEARCH_scalar( _what->avp_code, &vendor->list[1], avp.avp_code, 1, (struct dict_object *)NULL ); + SEARCH_scalar( _what->avp_code, &vendor->list[1], avp.avp_code, 1, (struct dict_object *)NULL ); } } break; @@ -979,13 +979,14 @@ case AVP_BY_NAME_ALL_VENDORS: { struct fd_list * li; + size_t wl = strlen((char *)what); /* First, search for vendor 0 */ - SEARCH_string( what, &dict->dict_vendors.list[2], avp.avp_name, 1); + SEARCH_os0_l( what, wl, &dict->dict_vendors.list[2], avp.avp_name, 1); /* If not found, loop for all vendors, until found */ for (li = dict->dict_vendors.list[0].next; li != &dict->dict_vendors.list[0]; li = li->next) { - SEARCH_string( what, &_O(li->o)->list[2], avp.avp_name, 1); + SEARCH_os0_l( what, wl, &_O(li->o)->list[2], avp.avp_name, 1); } } break; @@ -1007,7 +1008,7 @@ switch (criteria) { case CMD_BY_NAME: /* "what" is a command name */ - SEARCH_string( what, &dict->dict_cmd_name, cmd.cmd_name, 1); + SEARCH_os0( what, &dict->dict_cmd_name, cmd.cmd_name, 1); break; case CMD_BY_CODE_R: @@ -1095,7 +1096,7 @@ CHECK_PARAMS( verify_object(avp) && (avp->type == DICT_AVP) ); /* Perform the search */ - SEARCH_ruleavpname( avp->data.avp.avp_name, &parent->list[2]); + SEARCH_ruleavpname( avp->data.avp.avp_name, avp->datastr_len, &parent->list[2]); } break; @@ -1273,23 +1274,23 @@ dump_object( &dict->dict_vendors, 0, 3, 0 ); - fd_log_debug("###### Dumping applications #######\n"); + fd_log_debug("###### Dumping applications #######\n"); dump_object( &dict->dict_applications, 0, 1, 0 ); - fd_log_debug("###### Dumping types #######\n"); + fd_log_debug("###### Dumping types #######\n"); dump_list( &dict->dict_types, 0, 2, 0 ); - fd_log_debug("###### Dumping commands per name #######\n"); + fd_log_debug("###### Dumping commands per name #######\n"); dump_list( &dict->dict_cmd_name, 0, 2, 0 ); - fd_log_debug("###### Dumping commands per code and flags #######\n"); + fd_log_debug("###### Dumping commands per code and flags #######\n"); dump_list( &dict->dict_cmd_code, 0, 0, 0 ); - fd_log_debug("###### Statistics #######\n"); + fd_log_debug("###### Statistics #######\n"); for (i=1; i<=DICT_TYPE_MAX; i++) fd_log_debug(" %5d objects of type %s\n", dict->dict_count[i], dict_obj_info[i].name); @@ -1560,7 +1561,7 @@ /* Initialize the data of the new object */ init_object(new, type); - init_object_data(&new->data, data, type); + init_object_data(new, data, type); new->dico = dict; new->parent = parent; @@ -1631,7 +1632,7 @@ case DICT_RULE: /* A rule object is linked in list[2] of its parent command or AVP by the name of the AVP it refers */ - ret = fd_list_insert_ordered ( &parent->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpn, (void **)&locref ); + ret = fd_list_insert_ordered ( &parent->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpvc, (void **)&locref ); if (ret) goto error_unlock; break; @@ -1658,8 +1659,9 @@ /* We have a duplicate key in locref. Check if the pointed object is the same or not */ switch (type) { case DICT_VENDOR: - /* if we are here, it meas the two vendor id are identical */ - if (strcmp(locref->data.vendor.vendor_name, new->data.vendor.vendor_name)) { + /* if we are here, it means the two vendors id are identical */ + if (fd_os_cmp(locref->data.vendor.vendor_name, locref->datastr_len, + new->data.vendor.vendor_name, new->datastr_len)) { TRACE_DEBUG(FULL, "Conflicting vendor name"); break; } @@ -1669,7 +1671,8 @@ case DICT_APPLICATION: /* got same id */ - if (strcmp(locref->data.application.application_name, new->data.application.application_name)) { + if (fd_os_cmp(locref->data.application.application_name, locref->datastr_len, + new->data.application.application_name, new->datastr_len)) { TRACE_DEBUG(FULL, "Conflicting application name"); break; } @@ -1762,7 +1765,7 @@ break; case DICT_RULE: - /* Both rules point to the same AVPs */ + /* Both rules point to the same AVPs (code & vendor) */ if (locref->data.rule.rule_position != new->data.rule.rule_position) { TRACE_DEBUG(FULL, "Conflicting rule position"); break; @@ -1846,7 +1849,7 @@ { struct dictionary * new = NULL; - TRACE_ENTRY(""); + TRACE_ENTRY("%p", dict); /* Sanity checks */ ASSERT( (sizeof(type_base_name) / sizeof(type_base_name[0])) == (AVP_TYPE_MAX + 1) ); @@ -1864,13 +1867,17 @@ /* Initialize the sentinel for vendors and AVP lists */ init_object( &new->dict_vendors, DICT_VENDOR ); - new->dict_vendors.data.vendor.vendor_name = "(no vendor)"; + #define NO_VENDOR_NAME "(no vendor)" + new->dict_vendors.data.vendor.vendor_name = NO_VENDOR_NAME; + new->dict_vendors.datastr_len = CONSTSTRLEN(NO_VENDOR_NAME); new->dict_vendors.list[0].o = NULL; /* overwrite since element is also sentinel for this list. */ new->dict_vendors.dico = new; /* Initialize the sentinel for applications */ init_object( &new->dict_applications, DICT_APPLICATION ); - new->dict_applications.data.application.application_name = "Diameter Common Messages"; + #define APPLICATION_0_NAME "Diameter Common Messages" + new->dict_applications.data.application.application_name = APPLICATION_0_NAME; + new->dict_applications.datastr_len = CONSTSTRLEN(APPLICATION_0_NAME); new->dict_applications.list[0].o = NULL; /* overwrite since since element is also sentinel for this list. */ new->dict_applications.dico = new; @@ -1883,7 +1890,9 @@ /* Initialize the error command object */ init_object( &new->dict_cmd_error, DICT_COMMAND ); - new->dict_cmd_error.data.cmd.cmd_name="(generic error format)"; + #define GENERIC_ERROR_NAME "(generic error format)" + new->dict_cmd_error.data.cmd.cmd_name = GENERIC_ERROR_NAME; + new->dict_cmd_error.datastr_len = CONSTSTRLEN(GENERIC_ERROR_NAME); new->dict_cmd_error.data.cmd.cmd_flag_mask=CMD_FLAG_ERROR | CMD_FLAG_REQUEST | CMD_FLAG_RETRANSMIT; new->dict_cmd_error.data.cmd.cmd_flag_val =CMD_FLAG_ERROR; new->dict_cmd_error.dico = new;
--- a/libfdproto/fdproto-internal.h Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdproto/fdproto-internal.h Wed Feb 09 15:26:58 2011 +0900 @@ -64,7 +64,7 @@ extern pthread_rwlock_t fd_disp_lock; /* Messages / sessions API */ -int fd_sess_fromsid_msg ( unsigned char * sid, size_t len, struct session ** session, int * new); +int fd_sess_fromsid_msg ( uint8_t * sid, size_t len, struct session ** session, int * new); int fd_sess_ref_msg ( struct session * session ); int fd_sess_reclaim_msg ( struct session ** session );
--- a/libfdproto/init.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdproto/init.c Wed Feb 09 15:26:58 2011 +0900 @@ -35,9 +35,6 @@ #include "fdproto-internal.h" -/* Only for CPU cache flush */ -pthread_mutex_t fd_cpu_mtx_dummy = PTHREAD_MUTEX_INITIALIZER; - /* function to free the threadnames */ static void freelogstr(void * str) { if (TRACE_BOOL(ANNOYING)) {
--- a/libfdproto/lists.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdproto/lists.c Wed Feb 09 15:26:58 2011 +0900 @@ -162,129 +162,3 @@ } -/********************************************************************************************************/ -/* Hash function -- credits to Austin Appleby, thank you ^^ */ -/* See http://murmurhash.googlepages.com for more information on this function */ - -/* the strings are NOT always aligned properly (ex: received in RADIUS message), so we use the aligned MurmurHash2 function as needed */ -#define _HASH_MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; } -uint32_t fd_hash ( char * string, size_t len ) -{ - uint32_t hash = len; - char * data = string; - - const unsigned int m = 0x5bd1e995; - const int r = 24; - int align = (long)string & 3; - - if (!align || (len < 4)) { - - /* In case data is aligned, MurmurHash2 function */ - while(len >= 4) - { - /* Mix 4 bytes at a time into the hash */ - uint32_t k = *(uint32_t *)data; /* We don't care about the byte order */ - - _HASH_MIX(hash, k, m); - - data += 4; - len -= 4; - } - - /* Handle the last few bytes of the input */ - switch(len) { - case 3: hash ^= data[2] << 16; - case 2: hash ^= data[1] << 8; - case 1: hash ^= data[0]; - hash *= m; - } - - } else { - /* Unaligned data, use alignment-safe slower version */ - - /* Pre-load the temp registers */ - uint32_t t = 0, d = 0; - switch(align) - { - case 1: t |= data[2] << 16; - case 2: t |= data[1] << 8; - case 3: t |= data[0]; - } - t <<= (8 * align); - - data += 4-align; - len -= 4-align; - - /* From this point, "data" can be read by chunks of 4 bytes */ - - int sl = 8 * (4-align); - int sr = 8 * align; - - /* Mix */ - while(len >= 4) - { - uint32_t k; - - d = *(unsigned int *)data; - k = (t >> sr) | (d << sl); - - _HASH_MIX(hash, k, m); - - t = d; - - data += 4; - len -= 4; - } - - /* Handle leftover data in temp registers */ - d = 0; - if(len >= align) - { - uint32_t k; - - switch(align) - { - case 3: d |= data[2] << 16; - case 2: d |= data[1] << 8; - case 1: d |= data[0]; - } - - k = (t >> sr) | (d << sl); - _HASH_MIX(hash, k, m); - - data += align; - len -= align; - - /* Handle tail bytes */ - - switch(len) - { - case 3: hash ^= data[2] << 16; - case 2: hash ^= data[1] << 8; - case 1: hash ^= data[0]; - hash *= m; - }; - } - else - { - switch(len) - { - case 3: d |= data[2] << 16; - case 2: d |= data[1] << 8; - case 1: d |= data[0]; - case 0: hash ^= (t >> sr) | (d << sl); - hash *= m; - } - } - - - } - - /* Do a few final mixes of the hash to ensure the last few - bytes are well-incorporated. */ - hash ^= hash >> 13; - hash *= m; - hash ^= hash >> 15; - - return hash; -}
--- a/libfdproto/log.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdproto/log.c Wed Feb 09 15:26:58 2011 +0900 @@ -82,8 +82,14 @@ /* First, check if a value is already assigned to the current thread */ val = pthread_getspecific(fd_log_thname); + if (TRACE_BOOL(ANNOYING)) { + if (val) { + fd_log_debug("(Thread '%s' renamed to '%s')\n", (char *)val, name); + } else { + fd_log_debug("(Thread %p named '%s')\n", pthread_self(), name?:"(nil)"); + } + } if (val != NULL) { - TRACE_DEBUG(FULL, "Freeing old thread name: %s", val); free(val); }
--- a/libfdproto/messages.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdproto/messages.c Wed Feb 09 15:26:58 2011 +0900 @@ -125,7 +125,8 @@ void * data; struct timespec timeout; } msg_cb; /* Callback to be called when an answer is received, if not NULL */ - char * msg_src_id; /* Diameter Id of the peer this message was received from. This string is malloc'd and must be freed */ + DiamId_t msg_src_id; /* Diameter Id of the peer this message was received from. This string is malloc'd and must be freed */ + size_t msg_src_id_len; /* cached length of this string */ }; /* Macro to compute the message header size */ @@ -331,15 +332,16 @@ /* Add the Session-Id AVP if session is known */ if (sess && dict) { struct dict_object * sess_id_avp; - char * sid; + os0_t sid; + size_t sidlen; struct avp * avp; union avp_value val; CHECK_FCT( fd_dict_search( dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &sess_id_avp, ENOENT) ); - CHECK_FCT( fd_sess_getsid ( sess, &sid ) ); + CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen ) ); CHECK_FCT( fd_msg_avp_new ( sess_id_avp, 0, &avp ) ); - val.os.data = (unsigned char *)sid; - val.os.len = strlen(sid); + val.os.data = sid; + val.os.len = sidlen; CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_FIRST_CHILD, avp ) ); ans->msg_sess = sess; @@ -702,8 +704,8 @@ msg->msg_public.msg_hbhid, msg->msg_public.msg_eteid ); - fd_log_debug_fstr(fstr, INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p asso:%d sess:%p src:%s\n", - INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_associated, msg->msg_sess, msg->msg_src_id?:"(nil)"); + fd_log_debug_fstr(fstr, INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p asso:%d sess:%p src:%s(%zd)\n", + INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_associated, msg->msg_sess, msg->msg_src_id?:"(nil)", msg->msg_src_id_len); } /* Dump an avp object */ @@ -1011,16 +1013,21 @@ return (msg->msg_routable == 1) ? 1 : 0; } +/* cache the dictionary model for next function to avoid re-searching at every incoming message */ +static struct dict_object *cached_avp_rr_model = NULL; +static struct dictionary *cached_avp_rr_dict = NULL; +static pthread_mutex_t cached_avp_rr_lock = PTHREAD_MUTEX_INITIALIZER; + /* Associate source peer */ -int fd_msg_source_set( struct msg * msg, char * diamid, int add_rr, struct dictionary * dict ) +int fd_msg_source_set( struct msg * msg, DiamId_t diamid, size_t diamidlen, int add_rr, struct dictionary * dict ) { - TRACE_ENTRY( "%p %p %d %p", msg, diamid, add_rr, dict); + TRACE_ENTRY( "%p %p %zd %d %p", msg, diamid, diamidlen, add_rr, dict); /* Check we received a valid message */ CHECK_PARAMS( CHECK_MSG(msg) && ( (! add_rr) || dict ) ); /* Cleanup any previous source */ - free(msg->msg_src_id); msg->msg_src_id = NULL; + free(msg->msg_src_id); msg->msg_src_id = NULL; msg->msg_src_id_len = 0; /* If the request is to cleanup the source, we are done */ if (diamid == NULL) { @@ -1028,24 +1035,42 @@ } /* Otherwise save the new informations */ - CHECK_MALLOC( msg->msg_src_id = strdup(diamid) ); + CHECK_MALLOC( msg->msg_src_id = os0dup(diamid, diamidlen) ); + msg->msg_src_id_len = diamidlen; + if (add_rr) { - struct dict_object *avp_rr_model; + struct dict_object *avp_rr_model = NULL; avp_code_t code = AC_ROUTE_RECORD; struct avp *avp; union avp_value val; - /* Find the model for Route-Record in the dictionary */ - CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE, &code, &avp_rr_model, ENOENT) ); + /* Lock the cached values */ + CHECK_POSIX( pthread_mutex_lock(&cached_avp_rr_lock) ); + if (cached_avp_rr_dict == dict) { + avp_rr_model = cached_avp_rr_model; + } + CHECK_POSIX( pthread_mutex_unlock(&cached_avp_rr_lock) ); + + /* If it was not cached */ + if (!avp_rr_model) { + /* Find the model for Route-Record in the dictionary */ + CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE, &code, &avp_rr_model, ENOENT) ); + + /* Now cache this result */ + CHECK_POSIX( pthread_mutex_lock(&cached_avp_rr_lock) ); + cached_avp_rr_dict = dict; + cached_avp_rr_model = avp_rr_model; + CHECK_POSIX( pthread_mutex_unlock(&cached_avp_rr_lock) ); + } /* Create the AVP with this model */ CHECK_FCT( fd_msg_avp_new ( avp_rr_model, 0, &avp ) ); /* Set the AVP value with the diameter id */ memset(&val, 0, sizeof(val)); - val.os.data = (unsigned char *)diamid; - val.os.len = strlen(diamid); + val.os.data = (uint8_t *)diamid; + val.os.len = diamidlen; CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); /* Add the AVP in the message */ @@ -1056,9 +1081,9 @@ return 0; } -int fd_msg_source_get( struct msg * msg, char ** diamid ) +int fd_msg_source_get( struct msg * msg, DiamId_t* diamid, size_t * diamidlen ) { - TRACE_ENTRY( "%p %p", msg, diamid); + TRACE_ENTRY( "%p %p %p", msg, diamid, diamidlen); /* Check we received valid parameters */ CHECK_PARAMS( CHECK_MSG(msg) ); @@ -1067,6 +1092,9 @@ /* Copy the informations */ *diamid = msg->msg_src_id; + if (diamidlen) + *diamidlen = msg->msg_src_id_len; + /* done */ return 0; } @@ -2224,7 +2252,7 @@ goto out; /* Call all dispatch callbacks for a given message */ -int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, const char ** error_code) +int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, char ** error_code) { struct dictionary * dict; struct dict_object * app;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfdproto/ostr.c Wed Feb 09 15:26:58 2011 +0900 @@ -0,0 +1,267 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2011, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +#include "fdproto-internal.h" + +/* Similar to strdup with (must be verified) os0_t */ +os0_t os0dup_int(os0_t s, size_t l) { + os0_t r; + CHECK_MALLOC_DO( r = calloc(l+1, 1), return NULL ); + memcpy(r, s, l); /* this might be faster than a strcpy or strdup because it can work with 32 or 64b blocks */ + return r; +} + +/* case sensitive comparison, fast */ +int fd_os_cmp_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz) +{ + ASSERT( os1 && os2); + if (os1sz < os2sz) + return -1; + if (os1sz > os2sz) + return 1; + return memcmp(os1, os2, os1sz); +} + +/* a local version of tolower() that does not depend on LC_CTYPE locale */ +static inline uint8_t asciitolower(uint8_t a) +{ + if ((a >= 'A') && (a <= 'Z')) + return a + 32 /* == 'a' - 'A' */; + return a; +} + +/* a little less sensitive to case, slower. */ +int fd_os_almostcasecmp_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz) +{ + int i; + ASSERT( os1 && os2); + if (os1sz < os2sz) + return -1; + if (os1sz > os2sz) + return 1; + + for (i = 0; i < os1sz; i++) { + if (os1[i] == os2[i]) + continue; + + if (asciitolower(os1[i]) == asciitolower(os2[i])) + continue; + + return os1[i] < os2[i] ? -1 : 1; + } + + return 0; +} + +/* Check if the string contains only ASCII */ +int fd_os_is_valid_DiameterIdentity(uint8_t * os, size_t ossz) +{ + int i; + + /* Allow letters, digits, hyphen, dot */ + for (i=0; i < ossz; i++) { + if (os[i] > 'z') + break; + if (os[i] >= 'a') + continue; + if ((os[i] >= 'A') && (os[i] <= 'Z')) + continue; + if ((os[i] == '-') || (os[i] == '.')) + continue; + if ((os[i] >= '0') && (os[i] <= '9')) + continue; + break; + } + if (i < ossz) { + TRACE_DEBUG(INFO, "Invalid character '%c' in DiameterIdentity '%.*s'", os[i], ossz, os); + return 0; + } + return 1; +} + +/* The following function validates a string as a Diameter Identity or applies the IDNA transformation on it */ +int fd_os_validate_DiameterIdentity(char ** id, size_t * outsz, int memory /* 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static) */) +{ + TRACE_ENTRY("%p %p", id, outsz); + + *outsz = strlen(*id); + + if (!fd_os_is_valid_DiameterIdentity((os0_t)*id, *outsz)) { + char buf[HOST_NAME_MAX]; + + TODO("Stringprep in into buf"); + TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity, it was changed to '%s'", *id, buf); + TODO("Realloc *id if !memory"); + /* copy buf */ + /* update the size */ + return ENOTSUP; + } else { + if (memory == 1) { + CHECK_MALLOC( *id = os0dup(*id, *outsz) ); + } + } + return 0; +} + + + + + +/********************************************************************************************************/ +/* Hash function -- credits to Austin Appleby, thank you ^^ */ +/* See http://murmurhash.googlepages.com for more information on this function */ + +/* the strings are NOT always aligned properly (ex: received in RADIUS message), so we use the aligned MurmurHash2 function as needed */ +#define _HASH_MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; } +uint32_t fd_os_hash ( uint8_t * string, size_t len ) +{ + uint32_t hash = len; + uint8_t * data = string; + + const unsigned int m = 0x5bd1e995; + const int r = 24; + int align = (long)string & 3; + + if (!align || (len < 4)) { + /* In case data is aligned, MurmurHash2 function */ + while(len >= 4) + { + /* Mix 4 bytes at a time into the hash */ + uint32_t k = *(uint32_t *)data; /* We don't care about the byte order */ + + _HASH_MIX(hash, k, m); + + data += 4; + len -= 4; + } + + /* Handle the last few bytes of the input */ + switch(len) { + case 3: hash ^= data[2] << 16; + case 2: hash ^= data[1] << 8; + case 1: hash ^= data[0]; + hash *= m; + } + + } else { + /* Unaligned data, use alignment-safe slower version */ + + /* Pre-load the temp registers */ + uint32_t t = 0, d = 0; + switch(align) + { + case 1: t |= data[2] << 16; + case 2: t |= data[1] << 8; + case 3: t |= data[0]; + } + t <<= (8 * align); + + data += 4-align; + len -= 4-align; + + /* From this point, "data" can be read by chunks of 4 bytes */ + + int sl = 8 * (4-align); + int sr = 8 * align; + + /* Mix */ + while(len >= 4) + { + uint32_t k; + + d = *(unsigned int *)data; + k = (t >> sr) | (d << sl); + + _HASH_MIX(hash, k, m); + + t = d; + + data += 4; + len -= 4; + } + + /* Handle leftover data in temp registers */ + d = 0; + if(len >= align) + { + uint32_t k; + + switch(align) + { + case 3: d |= data[2] << 16; + case 2: d |= data[1] << 8; + case 1: d |= data[0]; + } + + k = (t >> sr) | (d << sl); + _HASH_MIX(hash, k, m); + + data += align; + len -= align; + + /* Handle tail bytes */ + + switch(len) + { + case 3: hash ^= data[2] << 16; + case 2: hash ^= data[1] << 8; + case 1: hash ^= data[0]; + hash *= m; + }; + } + else + { + switch(len) + { + case 3: d |= data[2] << 16; + case 2: d |= data[1] << 8; + case 1: d |= data[0]; + case 0: hash ^= (t >> sr) | (d << sl); + hash *= m; + } + } + + + } + + /* Do a few final mixes of the hash to ensure the last few + bytes are well-incorporated. */ + hash ^= hash >> 13; + hash *= m; + hash ^= hash >> 15; + + return hash; +} +
--- a/libfdproto/rt_data.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdproto/rt_data.c Wed Feb 09 15:26:58 2011 +0900 @@ -49,9 +49,11 @@ /* Items of the errors list */ struct rtd_error { - struct fd_list chain; /* link in the list, ordered by nexthop */ - char * nexthop;/* the peer the message was sent to */ - char * erh; /* the origin of the error */ + struct fd_list chain; /* link in the list, ordered by nexthop (fd_os_cmp) */ + DiamId_t nexthop;/* the peer the message was sent to */ + size_t nexthoplen; /* cached string length */ + DiamId_t erh; /* the origin of the error */ + size_t erhlen; /* cached string length */ uint32_t code; /* the error code */ }; @@ -106,19 +108,19 @@ return; } -/* Add a peer to the candidates list */ -int fd_rtd_candidate_add(struct rt_data * rtd, char * peerid, char * realm) +/* Add a peer to the candidates list. The source is our local peer list, so no need to care for the case here. */ +int fd_rtd_candidate_add(struct rt_data * rtd, DiamId_t peerid, size_t peeridlen, DiamId_t realm, size_t realmlen) { struct fd_list * prev; struct rtd_candidate * new; - TRACE_ENTRY("%p %p", rtd, peerid); - CHECK_PARAMS(rtd && peerid); + TRACE_ENTRY("%p %p %zd %p %zd", rtd, peerid, peeridlen, realm, realmlen); + CHECK_PARAMS(rtd && peerid && peeridlen); /* Since the peers are ordered when they are added (fd_g_activ_peers) we search for the position from the end -- this should be efficient */ for (prev = rtd->candidates.prev; prev != &rtd->candidates; prev = prev->prev) { struct rtd_candidate * cp = (struct rtd_candidate *) prev; - int cmp = strcmp(peerid, cp->diamid); + int cmp = fd_os_cmp(peerid, peeridlen, cp->diamid, cp->diamidlen); if (cmp > 0) break; if (cmp == 0) @@ -126,41 +128,39 @@ return 0; } + /* Create the new entry */ CHECK_MALLOC( new = malloc(sizeof(struct rtd_candidate)) ); memset(new, 0, sizeof(struct rtd_candidate) ); fd_list_init(&new->chain, NULL); - CHECK_MALLOC( new->diamid = strdup(peerid) ); - CHECK_MALLOC( new->realm = strdup(realm) ); + CHECK_MALLOC( new->diamid = os0dup(peerid, peeridlen) ) + new->diamidlen = peeridlen; + if (realm) { + CHECK_MALLOC( new->realm = os0dup(realm, realmlen) ) + new->realmlen = realmlen; + } + /* insert in the list at the correct position */ fd_list_insert_after(prev, &new->chain); return 0; } -/* Remove a peer from the candidates (if it is found) */ -void fd_rtd_candidate_del(struct rt_data * rtd, char * peerid, size_t sz /* if !0, peerid does not need to be \0 terminated */) +/* Remove a peer from the candidates (if it is found). Case insensitive search since the names are received from other peers */ +void fd_rtd_candidate_del(struct rt_data * rtd, uint8_t * id, size_t idsz) { struct fd_list * li; - TRACE_ENTRY("%p %p %zd", rtd, peerid, sz); - CHECK_PARAMS_DO( rtd && peerid , return ); + TRACE_ENTRY("%p %p %zd", rtd, id, idsz); + CHECK_PARAMS_DO( rtd && id && idsz, return ); + + if (!fd_os_is_valid_DiameterIdentity(id, idsz)) + /* it cannot be in the list */ + return; for (li = rtd->candidates.next; li != &rtd->candidates; li = li->next) { struct rtd_candidate * c = (struct rtd_candidate *) li; - int cmp; - if (sz) { - cmp = strncmp(peerid, c->diamid, sz); - if (!cmp) { - int len = strlen(c->diamid); - if (sz < len) - cmp = -1; - else if (sz == len) - cmp = 0; - else cmp = 1; - } - } else { - cmp = strcmp(peerid, c->diamid); - } + + int cmp = fd_os_almostcasecmp(id, idsz, c->diamid, c->diamidlen); if (!cmp) { /* Found it! Remove it */ @@ -174,7 +174,7 @@ if (cmp > 0) continue; - /* The list is ordered only if not extracted */ + /* The list is guaranteed to be ordered only if not extracted */ if (! rtd->extracted) break; } @@ -182,19 +182,20 @@ return; } -/* If a peer returned a protocol error for this message, save it so that we don't try to send it there again */ -int fd_rtd_error_add(struct rt_data * rtd, char * sentto, uint8_t * origin, size_t originsz, uint32_t rcode) +/* If a peer returned a protocol error for this message, save it so that we don't try to send it there again. + Case insensitive search since the names are received from other peers*/ +int fd_rtd_error_add(struct rt_data * rtd, DiamId_t sentto, size_t senttolen, uint8_t * origin, size_t originsz, uint32_t rcode) { struct fd_list * li; int match = 0; - TRACE_ENTRY("%p %p %p %d", rtd, sentto, origin, rcode); - CHECK_PARAMS( rtd && sentto ); /* origin may be NULL */ + TRACE_ENTRY("%p %p %zd %p %zd %u", rtd, sentto, senttolen, origin, originsz, rcode); + CHECK_PARAMS( rtd && sentto && senttolen ); /* origin may be NULL */ /* First add the new error entry */ for (li = rtd->errors.next; li != &rtd->errors; li = li->next) { struct rtd_error * e = (struct rtd_error *) li; - int cmp = strcmp(sentto, e->nexthop); + int cmp = fd_os_cmp(sentto, senttolen, e->nexthop, e->nexthoplen); if (cmp > 0) continue; if (!cmp) @@ -202,27 +203,40 @@ break; } - /* If we already had this entry, we should not have sent the message again to this peer... anyway... */ + /* If we already had this entry, we should not have sent the message again to this peer... anyway, let's close our eyes. */ + /* in the normal case, we save the error */ if (!match) { /* Add a new entry in the error list */ struct rtd_error * new; CHECK_MALLOC( new = malloc(sizeof(struct rtd_error)) ); memset(new, 0, sizeof(struct rtd_error)); fd_list_init(&new->chain, NULL); - CHECK_MALLOC(new->nexthop = strdup(sentto)); + + CHECK_MALLOC(new->nexthop = os0dup(sentto, senttolen)); + new->nexthoplen = senttolen; + if (origin) { - CHECK_MALLOC( new->erh = malloc(originsz + 1) ); - memcpy(new->erh, origin, originsz); - new->erh[originsz] = '\0'; + if (!originsz) { + originsz=strlen((char *)origin); + } else { + if (!fd_os_is_valid_DiameterIdentity(origin, originsz)){ + TRACE_DEBUG(FULL, "Received error %d from peer with invalid Origin-Host AVP, not saved", rcode); + origin = NULL; + goto after_origin; + } + } + CHECK_MALLOC( new->erh = (DiamId_t)os0dup(origin, originsz) ); + new->erhlen = originsz; } +after_origin: new->code = rcode; fd_list_insert_before(li, &new->chain); } /* Finally, remove this (these) peers from the candidate list */ - fd_rtd_candidate_del(rtd, sentto, 0); + fd_rtd_candidate_del(rtd, (os0_t)sentto, senttolen); if (origin) - fd_rtd_candidate_del(rtd, (char *)origin, originsz); + fd_rtd_candidate_del(rtd, origin, originsz); /* Done! */ return 0; @@ -273,7 +287,7 @@ /* Then we move the previous high score items at end of the list */ fd_list_move_end(candidates, &highest); - /* And the new high score is this reset */ + /* And the new high score is set */ hs = c->score; }
--- a/libfdproto/sessions.c Mon Jan 31 17:22:21 2011 +0900 +++ b/libfdproto/sessions.c Wed Feb 09 15:26:58 2011 +0900 @@ -68,7 +68,7 @@ struct session_handler { int eyec; /* An eye catcher also used to ensure the object is valid, must be SH_EYEC */ int id; /* A unique integer to identify this handler */ - void (*cleanup)(session_state *, char *, void *); /* The cleanup function to be called for cleaning a state */ + void (*cleanup)(session_state *, os0_t, void *); /* The cleanup function to be called for cleaning a state */ void *opaque; /* a value that is passed as is to the cleanup callback */ }; @@ -83,7 +83,7 @@ struct fd_list chain; /* Chaining in the list of session's states ordered by hdl->id */ union { struct session_handler *hdl; /* The handler for which this state was registered */ - char *sid; /* For deleted state, the sid of the session it belong to */ + os0_t sid; /* For deleted state, the sid of the session it belong to */ }; }; @@ -91,7 +91,8 @@ struct session { int eyec; /* Eyecatcher, SI_EYEC */ - char * sid; /* The \0-terminated Session-Id */ + os0_t sid; /* The \0-terminated Session-Id */ + size_t sidlen; /* cached length of sid */ uint32_t hash; /* computed hash of sid */ struct fd_list chain_h;/* chaining in the hash table of sessions. */ @@ -101,11 +102,12 @@ pthread_mutex_t stlock; /* A lock to protect the list of states associated with this session */ struct fd_list states; /* Sentinel for the list of states of this session. */ int msg_cnt;/* Reference counter for the messages pointing to this session */ + int is_destroyed; /* boolean telling if fd_sess_detroy has been called on this */ }; /* Sessions hash table, to allow fast sid to session retrieval */ static struct { - struct fd_list sentinel; /* sentinel element for this sublist */ + struct fd_list sentinel; /* sentinel element for this sublist. The sublist is ordered by hash value, then fd_os_cmp(sid). */ pthread_mutex_t lock; /* the mutex for this sublist -- we might probably change it to rwlock for a little optimization */ } sess_hash [ 1 << SESS_HASH_SIZE ] ; #define H_MASK( __hash ) ((__hash) & (( 1 << SESS_HASH_SIZE ) - 1)) @@ -131,12 +133,12 @@ /********************************************************************************************************/ -/* Initialize a session object. It is not linked now. sid must be already malloc'ed. */ -static struct session * new_session(char * sid, size_t sidlen) +/* Initialize a session object. It is not linked now. sid must be already malloc'ed. The hash has already been computed. */ +static struct session * new_session(os0_t sid, size_t sidlen, uint32_t hash) { struct session * sess; - TRACE_ENTRY("%p %d", sid, sidlen); + TRACE_ENTRY("%p %zd", sid, sidlen); CHECK_PARAMS_DO( sid && sidlen, return NULL ); CHECK_MALLOC_DO( sess = malloc(sizeof(struct session)), return NULL ); @@ -145,7 +147,8 @@ sess->eyec = SI_EYEC; sess->sid = sid; - sess->hash = fd_hash(sid, sidlen); + sess->sidlen = sidlen; + sess->hash = hash; fd_list_init(&sess->chain_h, sess); CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), return NULL ); @@ -157,6 +160,17 @@ return sess; } + +/* destroy the session object. It should really be already unlinked... */ +static void del_session(struct session * s) +{ + ASSERT(FD_IS_LIST_EMPTY(&s->states)); + free(s->sid); + fd_list_unlink(&s->chain_h); + fd_list_unlink(&s->expire); + CHECK_POSIX_DO( pthread_mutex_destroy(&s->stlock), /* continue */ ); + free(s); +} /* The expiry thread */ static void * exp_fct(void * arg) @@ -249,11 +263,14 @@ { TRACE_ENTRY(""); CHECK_FCT_DO( fd_thr_term(&exp_thr), /* continue */ ); + + /* Destroy all sessions in the hash table, and the hash table itself? -- How to do it without a race condition ? */ + return; } /* Create a new handler */ -int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(session_state * state, char * sid, void * opaque), void * opaque ) +int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(session_state *, os0_t, void *), void * opaque ) { struct session_handler *new; @@ -298,11 +315,11 @@ struct fd_list * li_si; CHECK_POSIX( pthread_mutex_lock(&sess_hash[i].lock) ); - for (li_si = sess_hash[i].sentinel.next; li_si != &sess_hash[i].sentinel; li_si = li_si->next) { + for (li_si = sess_hash[i].sentinel.next; li_si != &sess_hash[i].sentinel; li_si = li_si->next) { /* for each session in the hash line */ struct fd_list * li_st; struct session * sess = (struct session *)(li_si->o); CHECK_POSIX( pthread_mutex_lock(&sess->stlock) ); - for (li_st = sess->states.next; li_st != &sess->states; li_st = li_st->next) { + for (li_st = sess->states.next; li_st != &sess->states; li_st = li_st->next) { /* for each state in this session */ struct state * st = (struct state *)(li_st->o); /* The list is ordered */ if (st->hdl->id < del->id) @@ -310,7 +327,7 @@ if (st->hdl->id == del->id) { /* This state belongs to the handler we are deleting, move the item to the deleted_states list */ fd_list_unlink(&st->chain); - CHECK_MALLOC( st->sid = strdup(sess->sid) ); + st->sid = sess->sid; fd_list_insert_before(&deleted_states, &st->chain); } break; @@ -325,7 +342,6 @@ struct state * st = (struct state *)(deleted_states.next->o); TRACE_DEBUG(FULL, "Calling cleanup handler for session '%s' and data %p", st->sid, st->state); (*del->cleanup)(st->state, st->sid, del->opaque); - free(st->sid); fd_list_unlink(&st->chain); free(st); } @@ -342,37 +358,51 @@ /* Create a new session object with the default timeout value, and link it */ -int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t optlen ) +int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, uint8_t * opt, size_t optlen ) { - char * sid = NULL; + os0_t sid = NULL; size_t sidlen; + uint32_t hash; struct session * sess; struct fd_list * li; int found = 0; + int ret = 0; - TRACE_ENTRY("%p %p %p %d", session, diamId, opt, optlen); - CHECK_PARAMS( session && (diamId || opt) ); - + TRACE_ENTRY("%p %p %zd %p %zd", session, diamid, diamidlen, opt, optlen); + CHECK_PARAMS( session && (diamid || opt) ); + + if (diamid) { + if (!diamidlen) { + diamidlen = strlen(diamid); + } + /* We check if the string is a valid DiameterIdentity */ + CHECK_PARAMS( fd_os_is_valid_DiameterIdentity((uint8_t *)diamid, diamidlen) ); + } else { + diamidlen = 0; + } + if (opt) { + if (!optlen) { + optlen = strlen((char *)opt); + } else { + CHECK_PARAMS( fd_os_is_valid_os0(opt, optlen) ); + } + } else { + optlen = 0; + } + /* Ok, first create the identifier for the string */ - if (diamId == NULL) { + if (diamid == NULL) { /* opt is the full string */ - if (optlen) { - CHECK_MALLOC( sid = malloc(optlen + 1) ); - strncpy(sid, opt, optlen); - sid[optlen] = '\0'; - sidlen = optlen; - } else { - CHECK_MALLOC( sid = strdup(opt) ); - sidlen = strlen(sid); - } + CHECK_MALLOC( sid = os0dup(opt, optlen) ); + sidlen = optlen; } else { uint32_t sid_h_cpy; uint32_t sid_l_cpy; /* "<diamId>;<high32>;<low32>[;opt]" */ - sidlen = strlen(diamId); + sidlen = diamidlen; sidlen += 22; /* max size of ';<high32>;<low32>' */ if (opt) - sidlen += 1 + (optlen ?: strlen(opt)) ; + sidlen += 1 + optlen; /* ';opt' */ sidlen++; /* space for the final \0 also */ CHECK_MALLOC( sid = malloc(sidlen) ); @@ -384,34 +414,29 @@ CHECK_POSIX( pthread_mutex_unlock(&sid_lock) ); if (opt) { - if (optlen) - sidlen = snprintf(sid, sidlen, "%s;%u;%u;%.*s", diamId, sid_h_cpy, sid_l_cpy, (int)optlen, opt); - else - sidlen = snprintf(sid, sidlen, "%s;%u;%u;%s", diamId, sid_h_cpy, sid_l_cpy, opt); + sidlen = snprintf((char*)sid, sidlen, "%.*s;%u;%u;%.*s", (int)diamidlen, diamid, sid_h_cpy, sid_l_cpy, (int)optlen, opt); } else { - sidlen = snprintf(sid, sidlen, "%s;%u;%u", diamId, sid_h_cpy, sid_l_cpy); + sidlen = snprintf((char*)sid, sidlen, "%.*s;%u;%u", (int)diamidlen, diamid, sid_h_cpy, sid_l_cpy); } } - /* Initialize the session object now, to spend less time inside locked section later. - * Cons: we malloc then free if there is already a session with same SID; we could malloc later to avoid this. */ - CHECK_MALLOC( sess = new_session(sid, sidlen) ); + hash = fd_os_hash(sid, sidlen); /* Now find the place to add this object in the hash table. */ - CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) ); - pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) ); + CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash) ) ); + pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) ); - for (li = H_LIST(sess->hash)->next; li != H_LIST(sess->hash); li = li->next) { + for (li = H_LIST(hash)->next; li != H_LIST(hash); li = li->next) { int cmp; struct session * s = (struct session *)(li->o); /* The list is ordered by hash and sid (in case of collisions) */ - if (s->hash < sess->hash) + if (s->hash < hash) continue; - if (s->hash > sess->hash) + if (s->hash > hash) break; - cmp = strcasecmp(s->sid, sess->sid); + cmp = fd_os_cmp(s->sid, s->sidlen, sid, sidlen); if (cmp < 0) continue; if (cmp > 0) @@ -423,66 +448,78 @@ break; } - /* If the session did not exist, we can link it in global tables */ + /* If the session did not exist, we can create it & link it in global tables */ if (!found) { + CHECK_MALLOC_DO(sess = new_session(sid, sidlen, hash), + { + ret = ENOMEM; + goto out; + } ); + fd_list_insert_before(li, &sess->chain_h); /* hash table */ - - /* We must also insert in the expiry list */ - CHECK_POSIX( pthread_mutex_lock( &exp_lock ) ); - pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); - - /* Find the position in that list. We take it in reverse order */ - for (li = exp_sentinel.prev; li != &exp_sentinel; li = li->prev) { - struct session * s = (struct session *)(li->o); - if (TS_IS_INFERIOR( &s->timeout, &sess->timeout ) ) - break; + } else { + /* it was found: was it previously destroyed? */ + if ((*session)->is_destroyed == 0) { + ret = EALREADY; + goto out; + } else { + /* the session was marked destroyed, let's re-activate it. */ + TODO("Re-creating a deleted session. Should investigate if this can lead to an issue... (need more feedback)"); + sess = *session; + + /* update the expiry time */ + CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), { ASSERT(0); } ); + sess->timeout.tv_sec += SESS_DEFAULT_LIFETIME; } - fd_list_insert_after( li, &sess->expire ); - - /* We added a new expiring element, we must signal */ - if (li == &exp_sentinel) { - CHECK_POSIX_DO( pthread_cond_signal(&exp_cond), { ASSERT(0); } ); /* if it fails, we might not pop the cleanup handlers, but this should not happen -- and we'd have a serious problem otherwise */ - } + } - #if 0 - if (TRACE_BOOL(ANNOYING)) { - TRACE_DEBUG(FULL, "-- Updated session expiry list --"); - for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) { - struct session * s = (struct session *)(li->o); - fd_sess_dump(FULL, s); - } - TRACE_DEBUG(FULL, "-- end of expiry list --"); - } - #endif - - /* We're done */ - pthread_cleanup_pop(0); - CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); } ); /* if it fails, we might not pop the cleanup handler, but this should not happen -- and we'd have a serious problem otherwise */ + /* We must insert in the expiry list */ + CHECK_POSIX( pthread_mutex_lock( &exp_lock ) ); + pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); + + /* Find the position in that list. We take it in reverse order */ + for (li = exp_sentinel.prev; li != &exp_sentinel; li = li->prev) { + struct session * s = (struct session *)(li->o); + if (TS_IS_INFERIOR( &s->timeout, &sess->timeout ) ) + break; } - + fd_list_insert_after( li, &sess->expire ); + + /* We added a new expiring element, we must signal */ + if (li == &exp_sentinel) { + CHECK_POSIX_DO( pthread_cond_signal(&exp_cond), { ASSERT(0); } ); /* if it fails, we might not pop the cleanup handlers, but this should not happen -- and we'd have a serious problem otherwise */ + } + + /* We're done with the locked part */ pthread_cleanup_pop(0); - CHECK_POSIX( pthread_mutex_unlock( H_LOCK(sess->hash) ) ); + CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); } ); /* if it fails, we might not pop the cleanup handler, but this should not happen -- and we'd have a serious problem otherwise */ + +out: + ; + pthread_cleanup_pop(0); + CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) ); - /* If a session already existed, we must destroy the new element */ - if (found) { - CHECK_FCT( fd_sess_destroy( &sess ) ); /* we could avoid locking this time for optimization */ - return EALREADY; - } + if (ret) /* in case of error */ + return ret; *session = sess; return 0; } /* Find or create a session */ -int fd_sess_fromsid ( char * sid, size_t len, struct session ** session, int * new) +int fd_sess_fromsid ( uint8_t * sid, size_t len, struct session ** session, int * new) { int ret; TRACE_ENTRY("%p %d %p %p", sid, len, session, new); CHECK_PARAMS( sid && session ); + if (!fd_os_is_valid_os0(sid,len)) { + TRACE_DEBUG(INFO, "Warning: a Session-Id value contains \\0 chars... (len:%zd, begin:'%.*s')\n => Debug messages may be truncated.", len, len, sid); + } + /* All the work is done in sess_new */ - ret = fd_sess_new ( session, NULL, sid, len ); + ret = fd_sess_new ( session, NULL, 0, sid, len ); switch (ret) { case 0: case EALREADY: @@ -499,13 +536,15 @@ } /* Get the sid of a session */ -int fd_sess_getsid ( struct session * session, char ** sid ) +int fd_sess_getsid ( struct session * session, os0_t * sid, size_t * sidlen ) { TRACE_ENTRY("%p %p", session, sid); CHECK_PARAMS( VALIDATE_SI(session) && sid ); *sid = session->sid; + if (sidlen) + *sidlen = session->sidlen; return 0; } @@ -542,17 +581,6 @@ CHECK_POSIX_DO( pthread_cond_signal(&exp_cond), { ASSERT(0); /* so that we don't have a pending cancellation handler */ } ); } - #if 0 - if (TRACE_BOOL(ANNOYING)) { - TRACE_DEBUG(FULL, "-- Updated session expiry list --"); - for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) { - struct session * s = (struct session *)(li->o); - fd_sess_dump(FULL, s); - } - TRACE_DEBUG(FULL, "-- end of expiry list --"); - } - #endif - /* We're done */ pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) ); @@ -560,10 +588,16 @@ return 0; } -/* Destroy a session immediatly */ +/* Destroy the states associated to a session, and mark it destroyed. */ int fd_sess_destroy ( struct session ** session ) { struct session * sess; + int destroy_now; + os0_t sid; + int ret = 0; + + /* place to save the list of states to be cleaned up. We do it after finding them to avoid deadlocks. the "o" field becomes a copy of the sid. */ + struct fd_list deleted_states = FD_LIST_INITIALIZER( deleted_states ); TRACE_ENTRY("%p", session); CHECK_PARAMS( session && VALIDATE_SI(*session) ); @@ -571,29 +605,54 @@ sess = *session; *session = NULL; - /* Unlink and invalidate */ + /* Lock the hash line */ CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) ); pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) ); + + /* Unlink from the expiry list */ CHECK_POSIX_DO( pthread_mutex_lock( &exp_lock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } ); - fd_list_unlink( &sess->chain_h ); fd_list_unlink( &sess->expire ); /* no need to signal the condition here */ - sess->eyec = 0xdead; CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } ); + + /* Now move all states associated to this session into deleted_states */ + CHECK_POSIX_DO( pthread_mutex_lock( &sess->stlock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } ); + while (!FD_IS_LIST_EMPTY(&sess->states)) { + struct state * st = (struct state *)(sess->states.next->o); + fd_list_unlink(&st->chain); + fd_list_insert_before(&deleted_states, &st->chain); + } + CHECK_POSIX_DO( pthread_mutex_unlock( &sess->stlock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } ); + + /* Mark the session as destroyed */ + destroy_now = (sess->msg_cnt == 0); + if (destroy_now) { + fd_list_unlink( &sess->chain_h ); + sid = sess->sid; + } else { + sess->is_destroyed = 1; + CHECK_MALLOC_DO( sid = os0dup(sess->sid, sess->sidlen), ret = ENOMEM ); + } pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock( H_LOCK(sess->hash) ) ); - /* Now destroy all states associated -- we don't take the lock since nobody can access this session anymore (in theory) */ - while (!FD_IS_LIST_EMPTY(&sess->states)) { - struct state * st = (struct state *)(sess->states.next->o); + if (ret) + return ret; + + /* Now, really delete the states */ + while (!FD_IS_LIST_EMPTY(&deleted_states)) { + struct state * st = (struct state *)(deleted_states.next->o); fd_list_unlink(&st->chain); - TRACE_DEBUG(FULL, "Calling handler %p cleanup for state registered with session '%s'", st->hdl, sess->sid); - (*st->hdl->cleanup)(st->state, sess->sid, st->hdl->opaque); + TRACE_DEBUG(FULL, "Calling handler %p cleanup for state %p registered with session '%s'", st->hdl, st, sid); + (*st->hdl->cleanup)(st->state, sid, st->hdl->opaque); free(st); } - /* Finally, destroy the session itself */ - free(sess->sid); - free(sess); + /* Finally, destroy the session itself, if it is not referrenced by any message anymore */ + if (destroy_now) { + del_session(sess); + } else { + free(sid); + } return 0; } @@ -603,6 +662,7 @@ { struct session * sess; uint32_t hash; + int destroy_now = 0; TRACE_ENTRY("%p", session); CHECK_PARAMS( session && VALIDATE_SI(*session) ); @@ -611,20 +671,34 @@ hash = sess->hash; *session = NULL; - CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) ); - pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) ); + CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash) ) ); + pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) ); + CHECK_POSIX_DO( pthread_mutex_lock( &sess->stlock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } ); + pthread_cleanup_push( fd_cleanup_mutex, &sess->stlock ); CHECK_POSIX_DO( pthread_mutex_lock( &exp_lock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } ); + + /* We only do something if the states list is empty */ if (FD_IS_LIST_EMPTY(&sess->states)) { - fd_list_unlink( &sess->chain_h ); + /* In this case, we do as in destroy */ fd_list_unlink( &sess->expire ); - sess->eyec = 0xdead; - free(sess->sid); - free(sess); + destroy_now = (sess->msg_cnt == 0); + if (destroy_now) { + fd_list_unlink(&sess->chain_h); + } else { + /* just mark it as destroyed, it will be freed when the last message stops referencing it */ + sess->is_destroyed = 1; + } } + CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } ); pthread_cleanup_pop(0); + CHECK_POSIX_DO( pthread_mutex_unlock( &sess->stlock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } ); + pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) ); + if (destroy_now) + del_session(sess); + return 0; } @@ -637,7 +711,7 @@ int ret = 0; TRACE_ENTRY("%p %p %p", handler, session, state); - CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && state ); + CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && (!session->is_destroyed) && state ); /* Lock the session state list */ CHECK_POSIX( pthread_mutex_lock(&session->stlock) ); @@ -719,13 +793,13 @@ } /* For the messages module */ -int fd_sess_fromsid_msg ( unsigned char * sid, size_t len, struct session ** session, int * new) +int fd_sess_fromsid_msg ( uint8_t * sid, size_t len, struct session ** session, int * new) { TRACE_ENTRY("%p %zd %p %p", sid, len, session, new); CHECK_PARAMS( sid && len && session ); /* Get the session object */ - CHECK_FCT( fd_sess_fromsid ( (char *) sid, len, session, new) ); + CHECK_FCT( fd_sess_fromsid ( sid, len, session, new) ); /* Increase count */ CHECK_FCT( fd_sess_ref_msg ( *session ) ); @@ -785,7 +859,7 @@ fd_log_debug("\t %*s Invalid session object\n", level, ""); } else { - fd_log_debug("\t %*s sid '%s', hash %x\n", level, "", session->sid, session->hash); + fd_log_debug("\t %*s sid '%s'(%zd), hash %x\n", level, "", session->sid, session->sidlen, session->hash); strftime(buf, sizeof(buf), "%D,%T", localtime_r( &session->timeout.tv_sec , &tm )); fd_log_debug("\t %*s timeout %s.%09ld\n", level, "", buf, session->timeout.tv_nsec);
--- a/tests/CMakeLists.txt Mon Jan 31 17:22:21 2011 +0900 +++ b/tests/CMakeLists.txt Wed Feb 09 15:26:58 2011 +0900 @@ -12,7 +12,7 @@ # List the test cases SET(TEST_LIST testsctp - testlist + testostr testdict testmesg testfifo
--- a/tests/testdisp.c Mon Jan 31 17:22:21 2011 +0900 +++ b/tests/testdisp.c Wed Feb 09 15:26:58 2011 +0900 @@ -109,13 +109,14 @@ enum disp_action action; struct disp_hdl * hdl[NB_CB]; struct disp_when when; - const char * ec; + char * ec; /* First, initialize the daemon modules */ INIT_FD(); /* Create a dummy session, we don't use it anyway */ - CHECK( 0, fd_sess_new( &sess, "test.disp", NULL, 0 ) ); + #define DUMMY_SID "test.disp" + CHECK( 0, fd_sess_new( &sess, DUMMY_SID, CONSTSTRLEN(DUMMY_SID), NULL, 0 ) ); memset(&when, 0xff, sizeof(when)); /* check that we don't use un-initialized parts */
--- a/tests/testlist.c Mon Jan 31 17:22:21 2011 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/********************************************************************************************************* -* Software License Agreement (BSD License) * -* Author: Sebastien Decugis <sdecugis@nict.go.jp> * -* * -* Copyright (c) 2011, WIDE Project and NICT * -* All rights reserved. * -* * -* Redistribution and use of this software in source and binary forms, with or without modification, are * -* permitted provided that the following conditions are met: * -* * -* * Redistributions of source code must retain the above * -* copyright notice, this list of conditions and the * -* following disclaimer. * -* * -* * Redistributions in binary form must reproduce the above * -* copyright notice, this list of conditions and the * -* following disclaimer in the documentation and/or other * -* materials provided with the distribution. * -* * -* * Neither the name of the WIDE Project or NICT nor the * -* names of its contributors may be used to endorse or * -* promote products derived from this software without * -* specific prior written permission of WIDE Project and * -* NICT. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * -* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * -* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * -*********************************************************************************************************/ - -#include "tests.h" - -#define TEST_STR "This is my test string (with extra unused data)" -#define TEST_STRLEN 22 - -/* Main test routine */ -int main(int argc, char *argv[]) -{ - /* First, initialize the daemon modules */ - INIT_FD(); - - /* Check the hash function */ - { - char buf[30]; - - uint32_t hash = fd_hash(TEST_STR, TEST_STRLEN); /* reference value */ - - /* Check that a hash of a substring / surstring is different */ - CHECK( 1, hash != fd_hash(TEST_STR, TEST_STRLEN - 1) ? 1 : 0 ); - CHECK( 1, hash != fd_hash(TEST_STR, TEST_STRLEN + 1) ? 1 : 0 ); - - /* Check alignment of the string is not important */ - memcpy(buf + 4, TEST_STR, TEST_STRLEN); - CHECK( hash, fd_hash(buf + 4, TEST_STRLEN) ); - - memcpy(buf + 3, TEST_STR, TEST_STRLEN); - CHECK( hash, fd_hash(buf + 3, TEST_STRLEN) ); - - memcpy(buf + 2, TEST_STR, TEST_STRLEN); - CHECK( hash, fd_hash(buf + 2, TEST_STRLEN) ); - - memcpy(buf + 1, TEST_STR, TEST_STRLEN); - CHECK( hash, fd_hash(buf + 1, TEST_STRLEN) ); - } - - /* That's all for the tests yet */ - PASSTEST(); -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/testostr.c Wed Feb 09 15:26:58 2011 +0900 @@ -0,0 +1,73 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2011, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +#include "tests.h" + +#define TEST_STR (os0_t)"This is my test string (with extra unused data)" + +/* Main test routine */ +int main(int argc, char *argv[]) +{ + /* First, initialize the daemon modules */ + INIT_FD(); + + /* Check the hash function */ + { + uint8_t buf[30]; + + uint32_t hash = fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR)); /* reference value */ + + /* Check that a hash of a substring / surstring is different */ + CHECK( 1, hash != fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR) - 1) ? 1 : 0 ); + CHECK( 1, hash != fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR) + 1) ? 1 : 0 ); + + /* Check alignment of the string is not important */ + memcpy(buf + 4, TEST_STR, CONSTSTRLEN(TEST_STR)); + CHECK( hash, fd_os_hash(buf + 4, CONSTSTRLEN(TEST_STR)) ); + + memcpy(buf + 3, TEST_STR, CONSTSTRLEN(TEST_STR)); + CHECK( hash, fd_os_hash(buf + 3, CONSTSTRLEN(TEST_STR)) ); + + memcpy(buf + 2, TEST_STR, CONSTSTRLEN(TEST_STR)); + CHECK( hash, fd_os_hash(buf + 2, CONSTSTRLEN(TEST_STR)) ); + + memcpy(buf + 1, TEST_STR, CONSTSTRLEN(TEST_STR)); + CHECK( hash, fd_os_hash(buf + 1, CONSTSTRLEN(TEST_STR)) ); + } + + /* That's all for the tests yet */ + PASSTEST(); +} +
--- a/tests/testsctp.c Mon Jan 31 17:22:21 2011 +0900 +++ b/tests/testsctp.c Wed Feb 09 15:26:58 2011 +0900 @@ -55,12 +55,12 @@ /* In this case, we don't perform this simple test */ PASSTEST(); #else /* DISABLE_SCTP */ - int sock, srvsock, clisock; + struct cnxctx cli, srv; /* we use only their cc_socket & cc_state */ + int sock; char buf1[]="abcdef"; char *buf2; size_t sz; struct fd_list eps = FD_LIST_INITIALIZER(eps); - uint32_t status = 0; uint16_t str; int ev; @@ -79,6 +79,9 @@ freeaddrinfo(ai); } + memset(&cli, 0, sizeof(cli)); + memset(&srv, 0, sizeof(srv)); + /* First, initialize the daemon modules */ INIT_FD(); @@ -92,38 +95,38 @@ CHECK( 0, fd_sctp_listen( sock )); /* Now, create the client socket */ - CHECK( 0, fd_sctp_client( &clisock, 0, TEST_PORT, &eps )); + CHECK( 0, fd_sctp_client( &cli.cc_socket, 0, TEST_PORT, &eps )); /* Accept this connection */ - srvsock = accept(sock, NULL, NULL); + srv.cc_socket = accept(sock, NULL, NULL); /* Send a first message */ - CHECK( 0, fd_sctp_sendstr(srvsock, 1, (uint8_t *)buf1, sizeof(buf1), &status) ); - CHECK( 0, status); + CHECK( 0, fd_sctp_sendstr(&srv, 1, (uint8_t *)buf1, sizeof(buf1) ) ); + CHECK( 0, srv.cc_state); /* Receive this message */ redo1: - CHECK( 0, fd_sctp_recvmeta(clisock, &str, (uint8_t **)&buf2, &sz, &ev, &status) ); + CHECK( 0, fd_sctp_recvmeta(&cli, &str, (uint8_t **)&buf2, &sz, &ev) ); if (ev == FDEVP_CNX_EP_CHANGE) goto redo1; CHECK( FDEVP_CNX_MSG_RECV, ev); - CHECK( 0, status); + CHECK( 0, cli.cc_state); CHECK( 1, str); CHECK( sizeof(buf1), sz ); CHECK( 0, memcmp(buf1, buf2, sz) ); free(buf2); buf2 = NULL; /* Send in the other direction */ - CHECK( 0, fd_sctp_sendstr(clisock, 2, (uint8_t *)buf1, sizeof(buf1), &status) ); - CHECK( 0, status); + CHECK( 0, fd_sctp_sendstr(&cli, 2, (uint8_t *)buf1, sizeof(buf1)) ); + CHECK( 0, cli.cc_state); /* Receive this message */ redo2: - CHECK( 0, fd_sctp_recvmeta(srvsock, &str, (uint8_t **)&buf2, &sz, &ev, &status) ); + CHECK( 0, fd_sctp_recvmeta(&srv, &str, (uint8_t **)&buf2, &sz, &ev) ); if (ev == FDEVP_CNX_EP_CHANGE) goto redo2; CHECK( FDEVP_CNX_MSG_RECV, ev); - CHECK( 0, status); + CHECK( 0, srv.cc_state); CHECK( 2, str); CHECK( sizeof(buf1), sz ); CHECK( 0, memcmp(buf1, buf2, sz) );
--- a/tests/testsess.c Mon Jan 31 17:22:21 2011 +0900 +++ b/tests/testsess.c Wed Feb 09 15:26:58 2011 +0900 @@ -36,24 +36,26 @@ #include "tests.h" #define TEST_DIAM_ID "testsess.myid" -#define TEST_OPT "suffix" -#define TEST_SID TEST_DIAM_ID ";1234;5678;" TEST_OPT +#define TEST_OPT_IN "suffix" +#define TEST_OPT (os0_t)TEST_OPT_IN +#define TEST_SID_IN TEST_DIAM_ID ";1234;5678;" TEST_OPT_IN +#define TEST_SID (os0_t)TEST_SID_IN #define TEST_EYEC 0x7e57e1ec struct mystate { int eyec; /* TEST_EYEC */ - char * sid; /* the session with which the data was registered */ + os0_t sid; /* the session with which the data was registered */ int * freed; /* location where to write the freed status */ void * opaque; /* if opaque was provided, this is the value we expect */ }; -static void mycleanup( struct mystate * data, char * sid, void * opaque ) +static void mycleanup( struct mystate * data, os0_t sid, void * opaque ) { /* sanity */ CHECK( 1, sid ? 1 : 0 ); CHECK( 1, data? 1 : 0 ); CHECK( TEST_EYEC, data->eyec ); - CHECK( 0, strcmp(sid, data->sid) ); + CHECK( 0, strcmp((char *)sid, (char *)data->sid) ); if (data->freed) *(data->freed) += 1; if (data->opaque) { @@ -64,20 +66,25 @@ free(data); } -static __inline__ struct mystate * new_state(char * sid, int *freed) +static __inline__ struct mystate * new_state(os0_t sid, int *freed) { struct mystate *new; new = malloc(sizeof(struct mystate)); CHECK( 1, new ? 1 : 0 ); memset(new, 0, sizeof(struct mystate)); new->eyec = TEST_EYEC; - new->sid = strdup(sid); + new->sid = os0dup(sid, strlen((char *)sid)); CHECK( 1, new->sid ? 1 : 0 ); new->freed = freed; return new; } void * g_opaque = (void *)"test"; + +/* Avoid a lot of casts */ +#define strlen(s) strlen((char *)s) +#define strncmp(s1,s2,l) strncmp((char *)s1, (char *)s2, l) +#define strcmp(s1,s2) strcmp((char *)s1, (char *)s2) /* Main test routine */ @@ -85,7 +92,8 @@ { struct session_handler * hdl1, *hdl2; struct session *sess1, *sess2, *sess3; - char *str1, *str2; + os0_t str1, str2; + size_t str1len, str2len; int new; /* First, initialize the daemon modules */ @@ -108,57 +116,59 @@ /* Test Session Id generation (fd_sess_new) */ { /* DiamId is provided, not opt */ - CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, NULL, 0 ) ); - CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, NULL, 0 ) ); + CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) ); + CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) ); #if 0 fd_sess_dump(0, sess1); fd_sess_dump(0, sess2); #endif /* Check both string start with the diameter Id, but are different */ - CHECK( 0, fd_sess_getsid(sess1, &str1) ); - CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) ); - CHECK( 0, fd_sess_getsid(sess2, &str2) ); - CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) ); + CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) ); + CHECK( 1, (strlen(str1) == str1len) ? 1 : 0 ); + CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) ); + CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) ); + CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) ); CHECK( 1, strcmp(str1, str2) ? 1 : 0 ); CHECK( 0, fd_sess_destroy( &sess1 ) ); CHECK( 0, fd_sess_destroy( &sess2 ) ); /* diamId and opt */ - CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, TEST_OPT, 0 ) ); - CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, TEST_OPT, strlen(TEST_OPT) - 1 ) ); + CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, 0, TEST_OPT, 0 ) ); + CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), TEST_OPT, CONSTSTRLEN(TEST_OPT_IN) - 1 ) ); #if 0 fd_sess_dump(0, sess1); fd_sess_dump(0, sess2); #endif - CHECK( 0, fd_sess_getsid(sess1, &str1) ); - CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) ); - CHECK( 0, strcmp(str1 + strlen(str1) - strlen(TEST_OPT) - 1, ";" TEST_OPT) ); + CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) ); + CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) ); + CHECK( 0, strcmp(str1 + str1len - CONSTSTRLEN(TEST_OPT_IN) - 1, ";" TEST_OPT_IN) ); - CHECK( 0, fd_sess_getsid(sess2, &str2) ); - CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) ); - CHECK( 0, strncmp(str2 + strlen(str2) - strlen(TEST_OPT), ";" TEST_OPT, strlen(TEST_OPT)) ); + CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) ); + CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) ); + CHECK( 0, strncmp(str2 + str2len - CONSTSTRLEN(TEST_OPT_IN), ";" TEST_OPT_IN, CONSTSTRLEN(TEST_OPT_IN)) ); CHECK( 1, strcmp(str1, str2) ? 1 : 0 ); CHECK( 0, fd_sess_destroy( &sess1 ) ); CHECK( 0, fd_sess_destroy( &sess2 ) ); /* Now, only opt is provided */ - CHECK( 0, fd_sess_new( &sess1, NULL, TEST_SID, 0 ) ); - CHECK( EALREADY, fd_sess_new( &sess2, NULL, TEST_SID, 0 ) ); + CHECK( 0, fd_sess_new( &sess1, NULL, 0, TEST_SID, 0 ) ); + CHECK( EALREADY, fd_sess_new( &sess2, NULL, 0, TEST_SID, 0 ) ); CHECK( sess2, sess1 ); - CHECK( EALREADY, fd_sess_new( &sess3, NULL, TEST_SID, strlen(TEST_SID) ) ); + CHECK( EALREADY, fd_sess_new( &sess3, NULL, 0, TEST_SID, CONSTSTRLEN(TEST_SID_IN) ) ); CHECK( sess3, sess1 ); - CHECK( 0, fd_sess_new( &sess2, NULL, TEST_SID, strlen(TEST_SID) - 1 ) ); + CHECK( 0, fd_sess_new( &sess2, NULL, 0, TEST_SID, CONSTSTRLEN(TEST_SID_IN) - 1 ) ); #if 0 fd_sess_dump(0, sess1); fd_sess_dump(0, sess2); #endif - CHECK( 0, fd_sess_getsid(sess1, &str1) ); - CHECK( 0, fd_sess_getsid(sess2, &str2) ); - CHECK( 0, strncmp( str1, str2, strlen(TEST_SID) - 1 ) ); + CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) ); + CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) ); + CHECK( 0, strncmp( str1, str2, CONSTSTRLEN(TEST_SID_IN) - 1 ) ); CHECK( 0, strcmp( str1, TEST_SID ) ); + CHECK( 1, strcmp( str1, str2 ) ? 1 : 0 ); CHECK( 0, fd_sess_destroy( &sess2 ) ); CHECK( 0, fd_sess_destroy( &sess1 ) ); @@ -166,14 +176,14 @@ /* Test fd_sess_fromsid */ { - CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) ); + CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) ); CHECK( 1, new ? 1 : 0 ); - CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess2, &new ) ); + CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess2, &new ) ); CHECK( 0, new ); CHECK( sess1, sess2 ); - CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess3, NULL ) ); + CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess3, NULL ) ); CHECK( sess1, sess3 ); CHECK( 0, fd_sess_destroy( &sess1 ) ); @@ -183,13 +193,13 @@ { struct mystate *tms; - CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) ); + CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) ); CHECK( 1, new ? 1 : 0 ); CHECK( 0, fd_sess_reclaim( &sess1 ) ); CHECK( NULL, sess1 ); - CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) ); + CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) ); CHECK( 1, new ? 1 : 0 ); tms = new_state(TEST_SID, NULL); @@ -198,12 +208,12 @@ CHECK( 0, fd_sess_reclaim( &sess1 ) ); CHECK( NULL, sess1 ); - CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) ); + CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) ); CHECK( 0, new ); CHECK( 0, fd_sess_destroy( &sess1 ) ); - CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) ); + CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) ); CHECK( 1, new ? 1 : 0 ); CHECK( 0, fd_sess_destroy( &sess1 ) ); @@ -213,16 +223,16 @@ { struct timespec timeout; - CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) ); + CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) ); CHECK( 1, new ? 1 : 0 ); CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) ); - CHECK( 0, fd_sess_settimeout( sess1, &timeout) ); + CHECK( 0, fd_sess_settimeout( sess1, &timeout) ); /* expire now */ timeout.tv_sec = 0; timeout.tv_nsec= 50000000; /* 50 ms */ CHECK( 0, nanosleep(&timeout, NULL) ); - CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) ); + CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) ); CHECK( 1, new ? 1 : 0 ); CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) ); @@ -230,9 +240,9 @@ CHECK( 0, fd_sess_settimeout( sess1, &timeout) ); /* Create a second session */ - CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, NULL, 0 ) ); + CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, 0, NULL, 0 ) ); - /* We don't really have away to verify the expiry list is in proper order automatically here... */ + /* We don't really have a way to verify the expiry list is in proper order automatically here... */ CHECK( 0, fd_sess_destroy( &sess2 ) ); CHECK( 0, fd_sess_destroy( &sess1 ) ); @@ -247,12 +257,12 @@ void * testptr = NULL; /* Create three sessions */ - CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, NULL, 0 ) ); - CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, NULL, 0 ) ); - CHECK( 0, fd_sess_new( &sess3, TEST_DIAM_ID, NULL, 0 ) ); + CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, 0, NULL, 0 ) ); + CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, 0, NULL, 0 ) ); + CHECK( 0, fd_sess_new( &sess3, TEST_DIAM_ID, 0, NULL, 0 ) ); /* Create 2 states */ - CHECK( 0, fd_sess_getsid(sess1, &str1) ); + CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) ); freed[0] = 0; ms[0] = new_state(str1, &freed[0]); ms[1] = new_state(str1, NULL); @@ -280,17 +290,17 @@ /* Now create 6 states */ memset(&freed[0], 0, sizeof(freed)); - CHECK( 0, fd_sess_getsid(sess1, &str1) ); + CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) ); ms[0] = new_state(str1, &freed[0]); ms[1] = new_state(str1, &freed[1]); - CHECK( 0, fd_sess_getsid(sess2, &str1) ); + CHECK( 0, fd_sess_getsid(sess2, &str1, &str1len) ); ms[2] = new_state(str1, &freed[2]); ms[3] = new_state(str1, &freed[3]); - CHECK( 0, fd_sess_getsid(sess3, &str1) ); + CHECK( 0, fd_sess_getsid(sess3, &str1, &str1len) ); ms[4] = new_state(str1, &freed[4]); ms[5] = new_state(str1, &freed[5]); ms[5]->opaque = g_opaque; - str2 = strdup(str1); + str2 = os0dup(str1, str1len); CHECK( 1, str2 ? 1 : 0 ); /* Store the six states */ @@ -332,7 +342,7 @@ #endif /* Create again session 3, check that no data is associated to it */ - CHECK( 0, fd_sess_fromsid( str2, strlen(str2), &sess3, &new ) ); + CHECK( 0, fd_sess_fromsid( str2, str1len, &sess3, &new ) ); CHECK( 1, new ? 1 : 0 ); CHECK( 0, fd_sess_state_retrieve( hdl1, sess3, &tms ) ); CHECK( NULL, tms ); @@ -358,10 +368,12 @@ /* Check the last data can still be retrieved */ CHECK( 0, fd_sess_state_retrieve( hdl1, sess1, &tms ) ); - CHECK( 0, fd_sess_getsid(sess1, &str1) ); + CHECK( 1, tms ? 1 : 0); + CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) ); mycleanup(tms, str1, NULL); } + /* TODO: add tests on messages referencing sessions */ /* That's all for the tests yet */ PASSTEST();