int test_jid(xmpp_ctx_t *ctx) { char *node; char *domain; char *resource; node = xmpp_jid_node(ctx, jid1); domain = xmpp_jid_domain(ctx, jid1); resource = xmpp_jid_resource(ctx, jid1); printf("jid '%s' parsed to %s, %s, %s\n", jid1, node, domain, resource); if (strcmp(node, "foo")) return 1; if (strcmp(domain, "bar.com")) return 1; if (resource != NULL) return 1; if (node) xmpp_free(ctx, node); if (domain) xmpp_free(ctx, domain); if (resource) xmpp_free(ctx, resource); node = xmpp_jid_node(ctx, jid2); domain = xmpp_jid_domain(ctx, jid2); resource = xmpp_jid_resource(ctx, jid2); printf("jid '%s' parsed to %s, %s, %s\n", jid2, node, domain, resource); if (strcmp(node, "anyone")) return 1; if (strcmp(domain, "example.com")) return 1; if (strcmp(resource, "hullo")) return 1; if (node) xmpp_free(ctx, node); if (domain) xmpp_free(ctx, domain); if (resource) xmpp_free(ctx, resource); node = xmpp_jid_node(ctx, jid3); domain = xmpp_jid_domain(ctx, jid3); resource = xmpp_jid_resource(ctx, jid3); printf("jid '%s' parsed to %s, %s, %s\n", jid3, node, domain, resource); if (strcmp(node, "manic.porter")) return 1; if (strcmp(domain, "xyz.net")) return 1; if (strcmp(resource, "frob")) return 1; if (node) xmpp_free(ctx, node); if (domain) xmpp_free(ctx, domain); if (resource) xmpp_free(ctx, resource); node = xmpp_jid_node(ctx, jid4); domain = xmpp_jid_domain(ctx, jid4); resource = xmpp_jid_resource(ctx, jid4); printf("jid '%s' parsed to %s, %s, %s\n", jid4, node, domain, resource); if (node != NULL) return 1; if (strcmp(domain, "domain.tld")) return 1; if (resource != NULL) return 1; if (node) xmpp_free(ctx, node); if (domain) xmpp_free(ctx, domain); if (resource) xmpp_free(ctx, resource); return 0; }
/** Initiate a connection to the XMPP server. * This function returns immediately after starting the connection * process to the XMPP server, and notifiations of connection state changes * will be sent to the callback function. The domain and port to connect to * are usually determined by an SRV lookup for the xmpp-client service at * the domain specified in the JID. If SRV lookup fails, altdomain and * altport will be used instead if specified. * * @param conn a Strophe connection object * @param altdomain a string with domain to use if SRV lookup fails. If this * is NULL, the domain from the JID will be used. * @param altport an integer port number to use if SRV lookup fails. If this * is 0, the default port (5222) will be assumed. * @param callback a xmpp_conn_handler callback function that will receive * notifications of connection status * @param userdata an opaque data pointer that will be passed to the callback * * @return 0 on success and -1 on an error * * @ingroup Connections */ int xmpp_connect_client(xmpp_conn_t * const conn, const char * const altdomain, unsigned short altport, xmpp_conn_handler callback, void * const userdata) { char connectdomain[2048]; int connectport; const char * domain; conn->type = XMPP_CLIENT; conn->domain = xmpp_jid_domain(conn->ctx, conn->jid); if (!conn->domain) return -1; if (altdomain) { xmpp_debug(conn->ctx, "xmpp", "Connecting via altdomain."); strcpy(connectdomain, altdomain); connectport = altport ? altport : 5222; } else if (!sock_srv_lookup("xmpp-client", "tcp", conn->domain, connectdomain, 2048, &connectport)) { xmpp_debug(conn->ctx, "xmpp", "SRV lookup failed."); if (!altdomain) domain = conn->domain; else domain = altdomain; xmpp_debug(conn->ctx, "xmpp", "Using alternate domain %s, port %d", altdomain, altport); strcpy(connectdomain, domain); connectport = altport ? altport : 5222; } conn->sock = sock_connect(connectdomain, connectport); xmpp_debug(conn->ctx, "xmpp", "sock_connect to %s:%d returned %d", connectdomain, connectport, conn->sock); if (conn->sock == -1) return -1; /* setup handler */ conn->conn_handler = callback; conn->userdata = userdata; /* FIXME: it could happen that the connect returns immediately as * successful, though this is pretty unlikely. This would be a little * hard to fix, since we'd have to detect and fire off the callback * from within the event loop */ conn->state = XMPP_STATE_CONNECTING; conn->timeout_stamp = time_stamp(); xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", connectdomain); if (conn->xev) conn_ev_add_connect_handler(conn->xev); return 0; }
/** Initiate a connection to the XMPP server. * This function returns immediately after starting the connection * process to the XMPP server, and notifications of connection state changes * will be sent to the callback function. The domain and port to connect to * are usually determined by an SRV lookup for the xmpp-client service at * the domain specified in the JID. If SRV lookup fails, altdomain and * altport will be used instead if specified. * * @param conn a Strophe connection object * @param altdomain a string with domain to use if SRV lookup fails. If this * is NULL, the domain from the JID will be used. * @param altport an integer port number to use if SRV lookup fails. If this * is 0, the default port will be assumed. * @param callback a xmpp_conn_handler callback function that will receive * notifications of connection status * @param userdata an opaque data pointer that will be passed to the callback * * @return XMPP_EOK (0) on success or a number less than 0 on failure * * @ingroup Connections */ int xmpp_connect_client(xmpp_conn_t * const conn, const char * const altdomain, unsigned short altport, xmpp_conn_handler callback, void * const userdata) { resolver_srv_rr_t *srv_rr_list = NULL; resolver_srv_rr_t *rr; char *domain; const char *host = NULL; unsigned short port = 0; int found = XMPP_DOMAIN_NOT_FOUND; int rc; domain = xmpp_jid_domain(conn->ctx, conn->jid); if (!domain) return XMPP_EMEM; if (altdomain != NULL) { xmpp_debug(conn->ctx, "xmpp", "Connecting via altdomain."); host = altdomain; port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT); found = XMPP_DOMAIN_ALTDOMAIN; /* SSL tunneled connection on 5223 port is legacy and doesn't * have an SRV record. */ } else if (!conn->tls_legacy_ssl) { found = resolver_srv_lookup(conn->ctx, "xmpp-client", "tcp", domain, &srv_rr_list); } if (XMPP_DOMAIN_NOT_FOUND == found) { xmpp_debug(conn->ctx, "xmpp", "SRV lookup failed, " "connecting via domain."); host = domain; port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT); found = XMPP_DOMAIN_ALTDOMAIN; } rr = srv_rr_list; do { if (XMPP_DOMAIN_FOUND == found && rr != NULL) { host = rr->target; port = rr->port; rr = rr->next; } rc = _conn_connect(conn, domain, host, port, XMPP_CLIENT, callback, userdata); } while (rc != 0 && rr != NULL); xmpp_free(conn->ctx, domain); resolver_srv_free(conn->ctx, srv_rr_list); return rc; }
/** Initiate a connection to the XMPP server. * This function returns immediately after starting the connection * process to the XMPP server, and notifiations of connection state changes * will be sent to the callback function. The domain and port to connect to * are usually determined by an SRV lookup for the xmpp-client service at * the domain specified in the JID. If SRV lookup fails, altdomain and * altport will be used instead if specified. * * @param conn a Strophe connection object * @param altdomain a string with domain to use if SRV lookup fails. If this * is NULL, the domain from the JID will be used. * @param altport an integer port number to use if SRV lookup fails. If this * is 0, the default port will be assumed. * @param callback a xmpp_conn_handler callback function that will receive * notifications of connection status * @param userdata an opaque data pointer that will be passed to the callback * * @return 0 on success and -1 on an error * * @ingroup Connections */ int xmpp_connect_client(xmpp_conn_t * const conn, const char * const altdomain, unsigned short altport, xmpp_conn_handler callback, void * const userdata) { char domain[2048]; int port; const char *prefdomain = NULL; int found; if (conn->state != XMPP_STATE_DISCONNECTED) return -1; if (conn->domain != NULL) xmpp_free(conn->ctx, conn->domain); conn->type = XMPP_CLIENT; conn->secured = 0; conn->tls_failed = 0; conn->domain = xmpp_jid_domain(conn->ctx, conn->jid); if (!conn->domain) return -1; if (altdomain != NULL) { xmpp_debug(conn->ctx, "xmpp", "Connecting via altdomain."); prefdomain = altdomain; port = altport ? altport : _conn_default_port(conn); } else { found = sock_srv_lookup("xmpp-client", "tcp", conn->domain, domain, sizeof(domain), &port); if (!found) { xmpp_debug(conn->ctx, "xmpp", "SRV lookup failed, " "connecting via domain."); prefdomain = conn->domain; port = altport ? altport : _conn_default_port(conn); } if (conn->tls_legacy_ssl) { /* SSL tunneled connection on 5223 port is legacy and doesn't * have an SRV record. Force port 5223 here unless altport is * specified. */ port = altport ? altport : XMPP_PORT_CLIENT_LEGACY_SSL; } } if (prefdomain != NULL) { strncpy(domain, prefdomain, sizeof(domain)); domain[sizeof(domain) - 1] = '\0'; } conn->sock = sock_connect(domain, port); xmpp_debug(conn->ctx, "xmpp", "sock_connect to %s:%d returned %d", domain, port, conn->sock); if (conn->sock == -1) return -1; /* setup handler */ conn->conn_handler = callback; conn->userdata = userdata; /* FIXME: it could happen that the connect returns immediately as * successful, though this is pretty unlikely. This would be a little * hard to fix, since we'd have to detect and fire off the callback * from within the event loop */ conn->state = XMPP_STATE_CONNECTING; conn->timeout_stamp = time_stamp(); xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", domain); return 0; }
/** generate auth response string for the SASL DIGEST-MD5 mechanism */ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge, const char *jid, const char *password) { hash_t *table; char *result = NULL; char *node, *domain, *realm; char *value; char *response; int rlen; struct MD5Context MD5; unsigned char digest[16], HA1[16], HA2[16]; char hex[32]; char cnonce[13]; /* our digest response is Hex( KD( HEX(MD5(A1)), nonce ':' nc ':' cnonce ':' qop ':' HEX(MD5(A2)) )) where KD(k, s) = MD5(k ':' s), A1 = MD5( node ':' realm ':' password ) ':' nonce ':' cnonce A2 = "AUTHENTICATE" ':' "xmpp/" domain If there is an authzid it is ':'-appended to A1 */ /* parse the challenge */ table = _parse_digest_challenge(ctx, challenge); if (table == NULL) { xmpp_error(ctx, "SASL", "couldn't parse digest challenge"); return NULL; } node = xmpp_jid_node(ctx, jid); domain = xmpp_jid_domain(ctx, jid); /* generate default realm of domain if one didn't come from the server */ realm = hash_get(table, "realm"); if (realm == NULL || strlen(realm) == 0) { hash_add(table, "realm", xmpp_strdup(ctx, domain)); realm = hash_get(table, "realm"); } /* add our response fields */ hash_add(table, "username", xmpp_strdup(ctx, node)); xmpp_rand_nonce(ctx->rand, cnonce, sizeof(cnonce)); hash_add(table, "cnonce", xmpp_strdup(ctx, cnonce)); hash_add(table, "nc", xmpp_strdup(ctx, "00000001")); hash_add(table, "qop", xmpp_strdup(ctx, "auth")); value = xmpp_alloc(ctx, 5 + strlen(domain) + 1); memcpy(value, "xmpp/", 5); memcpy(value+5, domain, strlen(domain)); value[5+strlen(domain)] = '\0'; hash_add(table, "digest-uri", value); /* generate response */ /* construct MD5(node : realm : password) */ MD5Init(&MD5); MD5Update(&MD5, (unsigned char *)node, strlen(node)); MD5Update(&MD5, (unsigned char *)":", 1); MD5Update(&MD5, (unsigned char *)realm, strlen(realm)); MD5Update(&MD5, (unsigned char *)":", 1); MD5Update(&MD5, (unsigned char *)password, strlen(password)); MD5Final(digest, &MD5); /* digest now contains the first field of A1 */ MD5Init(&MD5); MD5Update(&MD5, digest, 16); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "nonce"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "cnonce"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Final(digest, &MD5); /* now digest is MD5(A1) */ memcpy(HA1, digest, 16); /* construct MD5(A2) */ MD5Init(&MD5); MD5Update(&MD5, (unsigned char *)"AUTHENTICATE:", 13); value = hash_get(table, "digest-uri"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); if (strcmp(hash_get(table, "qop"), "auth") != 0) { MD5Update(&MD5, (unsigned char *)":00000000000000000000000000000000", 33); } MD5Final(digest, &MD5); memcpy(HA2, digest, 16); /* construct response */ MD5Init(&MD5); _digest_to_hex((char *)HA1, hex); MD5Update(&MD5, (unsigned char *)hex, 32); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "nonce"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "nc"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "cnonce"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "qop"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Update(&MD5, (unsigned char *)":", 1); _digest_to_hex((char *)HA2, hex); MD5Update(&MD5, (unsigned char *)hex, 32); MD5Final(digest, &MD5); response = xmpp_alloc(ctx, 32+1); _digest_to_hex((char *)digest, hex); memcpy(response, hex, 32); response[32] = '\0'; hash_add(table, "response", response); /* construct reply */ result = NULL; rlen = 0; result = _add_key(ctx, table, "username", result, &rlen, 1); result = _add_key(ctx, table, "realm", result, &rlen, 1); result = _add_key(ctx, table, "nonce", result, &rlen, 1); result = _add_key(ctx, table, "cnonce", result, &rlen, 1); result = _add_key(ctx, table, "nc", result, &rlen, 0); result = _add_key(ctx, table, "qop", result, &rlen, 0); result = _add_key(ctx, table, "digest-uri", result, &rlen, 1); result = _add_key(ctx, table, "response", result, &rlen, 0); result = _add_key(ctx, table, "charset", result, &rlen, 0); xmpp_free(ctx, node); xmpp_free(ctx, domain); hash_release(table); /* also frees value strings */ /* reuse response for the base64 encode of our result */ response = xmpp_base64_encode(ctx, (unsigned char *)result, strlen(result)); xmpp_free(ctx, result); return response; }