📄 mapi.c
字号:
} return strdup(errstr);}#endif /* HAVE_OPENSSL *//* (Re-)establish a connection with the server. */static MapiMsgconnect_to_server(Mapi mid){ struct sockaddr_in server;#ifdef HAVE_SYS_UN_H struct sockaddr_un userver;#endif struct sockaddr *serv; socklen_t servsize; SOCKET s;#ifdef HAVE_OPENSSL SSL *ssl = NULL;#endif if (mid->connected) close_connection(mid);#ifdef HAVE_SYS_UN_H if (#ifdef HAVE_OPENSSL !mid->secure &&#endif mid->hostname && mid->hostname[0] == '/') { if (strlen(mid->hostname) >= sizeof(userver.sun_path)) { return mapi_setError(mid, "path name too long", "mapi_reconnect", MERROR); } userver.sun_family = AF_UNIX; strncpy(userver.sun_path, mid->hostname, sizeof(userver.sun_path)); serv = (struct sockaddr *) &userver; servsize = sizeof(userver); } else#endif { struct hostent *hp; hp = gethostbyname(mid->hostname); if (hp == NULL) { return mapi_setError(mid, "gethostbyname failed", "mapi_reconnect", MERROR); } memset(&server, 0, sizeof(server)); memcpy(&server.sin_addr, hp->h_addr, hp->h_length); server.sin_family = hp->h_addrtype; server.sin_port = htons((unsigned short) (mid->port & 0xFFFF)); serv = (struct sockaddr *) &server; servsize = sizeof(server); }#ifdef HAVE_OPENSSL if (mid->secure && mapi_ssl_ctx == NULL) { mapi_ssl_ctx = SSL_CTX_new(SSLv23_method()); if (mapi_ssl_ctx == 0) { char *errstr = ssl_error(SSL_ERROR_SSL, 0); mapi_setError(mid, errstr, "mapi_reconnect", MERROR); free(errstr); return mid->error; } SSL_CTX_set_verify(mapi_ssl_ctx, SSL_VERIFY_NONE, NULL); }#endif s = socket(serv->sa_family, SOCK_STREAM, IPPROTO_TCP); if (s < 0) { return mapi_setError(mid, "Open socket failed", "mapi_reconnect", MERROR); } if (connect(s, serv, servsize) < 0) {#ifdef NATIVE_WIN32 fprintf(stderr, "!ERROR mapi_reconnect: connect: error %d\n", WSAGetLastError());#else perror("!ERROR mapi_reconnect: connect");#endif return mapi_setError(mid, "Setup connection failed", "mapi_reconnect", MERROR); }#ifdef HAVE_OPENSSL if (mid->secure) { if ((ssl = SSL_new(mapi_ssl_ctx)) == 0) { char *errstr = ssl_error(SSL_ERROR_SSL, 0); mapi_setError(mid, errstr, "mapi_reconnect", MERROR); free(errstr); close(s); return mid->error; } if (!SSL_set_fd(ssl, s)) { char *errstr = ssl_error(SSL_ERROR_SSL, 0); mapi_setError(mid, errstr, "mapi_reconnect", MERROR); free(errstr); SSL_free(ssl); close(s); return mid->error; } SSL_set_connect_state(ssl); for (;;) { int ret, err; char *errstr; ret = SSL_connect(ssl); err = SSL_get_error(ssl, ret); switch (err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: /* try again */ continue; case SSL_ERROR_NONE: /* successful connect */ break; case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: default: /* some error occurred */ SSL_free(ssl); close(s); errstr = ssl_error(err, ret); mapi_setError(mid, errstr, "mapi_reconnect", MERROR); free(errstr); return mid->error; } break; } mid->to = ssl_wastream(ssl, "Mapi client write"); mid->from = ssl_rastream(ssl, "Mapi client read"); } else#endif { mid->to = socket_wastream(s, "Mapi client write"); mid->from = socket_rastream(s, "Mapi client read"); } check_stream(mid, mid->to, "Cannot open socket for writing", "mapi_reconnect", mid->error); check_stream(mid, mid->from, "Cannot open socket for reading", "mapi_reconnect", mid->error); mid->connected = 1; return MOK;}MapiMsgmapi_start_talking(Mapi mid){ char buf[BLOCK]; size_t len; MapiHdl hdl; int pversion = 0; char *chal; char *server; char *protover; char *rest; mid->to = block_stream(mid->to); check_stream(mid, mid->to, stream_error(mid->to), "mapi_start_talking", mid->error); mid->from = block_stream(mid->from); check_stream(mid, mid->from, stream_error(mid->from), "mapi_start_talking", mid->error); /* consume server challenge */ len = stream_read_block(mid->from, buf, 1, BLOCK); check_stream(mid, mid->from, "Connection terminated", "mapi_start_talking", (mid->blk.eos = 1, mid->error)); assert(len < BLOCK); /* buf at this point looks like "challenge:servertype:protover[:.*]" */ chal = buf; server = strchr(chal, ':'); if (server == NULL) { mapi_setError(mid, "Challenge string is not valid", "mapi_start_talking", MERROR); return mid->error; } *server++ = '\0'; protover = strchr(server, ':'); if (protover == NULL) { mapi_setError(mid, "Challenge string is not valid", "mapi_start_talking", MERROR); return mid->error; } *protover++ = '\0'; rest = strchr(protover, ':'); if (rest != NULL) { *rest++ = '\0'; } pversion = atoi(protover); if (pversion < 8) { /* because the headers changed, and because it makes no sense to * try and be backwards compatible, we bail out with a friendly * message saying so. */ snprintf(buf, BLOCK, "Unsupported protocol version: %d. " "This client only supports version 8 and up. " "Sorry, can't help you here!", pversion); mapi_setError(mid, buf, "mapi_start_talking", MERROR); return mid->error; } else if (pversion == 8) { char* hash = NULL; char* hashes = NULL; /* the database has sent a list of supported hashes to us, it's * in the form of a comma separated list and in the variable * rest. We try to use the strongest algorithm. */ hashes = rest; hash = strchr(hashes, ':'); /* temp misuse hash */ if (hash) { *hash = '\0'; rest = hash + 1; } hash = NULL; /* TODO: make this actually obey the separation by commas, and * only allow full matches */ if (1 == 0) { /* language construct issue */#ifdef HAVE_OPENSSL } else if (strstr(hashes, "SHA1") != NULL) { /* The SHA-1 RSA hash algorithm is a 160 bit hash. In order to * use in a string, a hexadecimal representation of the bit * sequence is used. */ unsigned char md[20]; /* should be SHA_DIGEST_LENGTH */ int n = strlen(mid->password) + strlen(chal); char key[n]; strcpy(key, mid->password); strncat(key, chal, strlen(chal)); SHA1((unsigned char*)key, n, md); hash = malloc(sizeof(char) * (/*{SHA1}*/6 + 20 * 2 + 1)); sprintf(hash, "{SHA1}%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", md[0], md[1], md[2], md[3], md[4], md[5], md[6], md[7], md[8], md[9], md[10], md[11], md[12], md[13], md[14], md[15], md[16], md[17], md[18], md[19] ); } else if (strstr(hashes, "MD5") != NULL) { /* The MD5 hash algorithm is a 128 bit hash. In order to * use in a string, a hexadecimal representation of the bit * sequence is used. */ unsigned char md[16]; /* should be MD5_DIGEST_LENGTH */ int n = strlen(mid->password) + strlen(chal); char key[n]; strcpy(key, mid->password); strncat(key, chal, strlen(chal)); MD5((unsigned char*)key, n, md); hash = malloc(sizeof(char) * (/*{MD5}*/5 + 16 * 2 + 1)); sprintf(hash, "{MD5}%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x", md[0], md[1], md[2], md[3], md[4], md[5], md[6], md[7], md[8], md[9], md[10], md[11], md[12], md[13], md[14], md[15] );#endif#ifdef HAVE_CRYPT } else if (strstr(hashes, "crypt") != NULL) { /* The crypt hash algorithm uses UNIX crypt, a modification of * DES which uses a 2-char wide salt. Because crypt only cares * about the first eight characters of the given password, the * challenge may not be taken into account at all. As salt, the * last two characters of the challenge are used. */ char key[8]; /* NULL termination is not necessary */ char salt[3]; /* NULL termination is a necessity! */ char* cr; int n; /* prepare the key */ n = strlen(mid->password); if (n >= 8) { strncpy(key, mid->password, 8); } else { /* pad with the challenge, we know it is always 8+ chars */ strncpy(key, mid->password, n); strncpy(key + n, chal, 8 - n); } /* prepare the salt */ n = strlen(chal); salt[0] = chal[n - 2]; salt[1] = chal[n - 1]; salt[2] = '\0'; /* call crypt to do the work */ cr = crypt(key, salt); assert (cr != NULL); hash = malloc(sizeof(char) * (/*{crypt}*/7 + strlen(cr) + 1)); sprintf(hash, "{crypt}%s", cr);#endif } else if (strstr(hashes, "plain") != NULL) { /* The plain text algorithm, doesn't really hash at all. It's * the easiest algorithm, as it just appends the challenge to * the password and returns it. */ hash = malloc(sizeof(char) * (/*{plain}*/7 + strlen(mid->password) + strlen(chal) + 1)); sprintf(hash, "{plain}%s%s", mid->password, chal); } /* could use else here, but below looks cleaner */ if (hash == NULL) { char *algo = strdup(rest); /* the server doesn't support what we do (no plain?!?) */ snprintf(buf, BLOCK, "unsupported hash algorithms: %s", algo); free(algo); close_connection(mid); return mapi_setError(mid, buf, "mapi_start_talking", MERROR); } /* in rest now should be the byte order of the server */ stream_set_byteorder(mid->from, strncmp(rest, "BIG", 3) == 0); /* note: if we make the database field an empty string, it * means we want the default. However, it *should* be there. */ snprintf(buf, BLOCK, "%s:%s:%s:%s:%s:\n",#ifdef WORDS_BIGENDIAN "BIG",#else "LIT",#endif mid->username, hash, mid->language, mid->database == NULL ? "" : mid->database); free(hash); } if (mid->trace == MAPI_TRACE) { printf("sending first request [%d]:%s", BLOCK, buf); fflush(stdout); } len = strlen(buf); stream_write(mid->to, buf, 1, len); check_stream(mid, mid->to, "Could not send initial byte sequence", "mapi_start_talking", mid->error); stream_flush(mid->to); check_stream(mid, mid->to, "Could not send initial byte sequence", "mapi_start_talking", mid->error); /* consume the welcome message from the server */ hdl = mapi_new_handle(mid); mid->active = hdl; read_into_cache(hdl, 0); if (mid->error) { if (hdl) { /* propagate error from result to mid mapi_close_handle clears the errors, so save them first*/ char *errorstr = hdl->result ? hdl->result->errorstr : mid->errorstr; MapiMsg error = mid->error; if (hdl->result) hdl->result->errorstr = NULL; /* clear these so errorstr doesn't get freed */ mid->errorstr = NULL; mapi_close_handle(hdl); mapi_setError(mid, errorstr, "mapi_start_talking", error); free(errorstr); /* now free it after a copy has been made */ } return mid->error; } if (hdl->result && hdl->result->cache.line) { int i; size_t motdlen = 0; struct MapiResultSet *result = hdl->result; for (i = 0; i < result->cache.writer; i++) { if (result->cache.line[i].rows) { switch (result->cache.line[i].rows[0]) { case '#': motdlen += strlen(result->cache.line[i].rows) + 1; break; case '^': { /* FIXME: there may be multiple redirects */ char *tmp = result->cache.line[i].rows; char *tmp2 = ""; /* redirect, looks like: * ^mapi:monetdb://localhost:50001/test?lang=sql&user=monetdb */ /* first see if we reached our redirection limit */ if (mid->redircnt > 10) { mapi_close_handle(hdl); mapi_setError(mid, "too many redirects", "mapi_start_talking", MERROR); return(mid->error); } /* see if we can possibly handle the redirect */ tmp++; if (strncmp("mapi:monetdb", tmp, 12) != 0) goto err; /* parse components (we store the args * immediately in the mid... ok, that's dirty */ tmp += strlen("mapi:monetdb://"); mid->hostname = tmp; if ((tmp = strchr(tmp, ':')) != NULL) { *tmp++ = '\0'; tmp2 = tmp; } else { tmp = mid->hostname; } if ((tmp = strchr(tmp, '/')) != NULL) { *tmp++ = '\0'; mid->port = atoi(tmp2); if (mid->port == 0) goto err; mid->database = tmp; } else { tmp = mid->hostname; goto err; } if ((tmp = strchr(tmp, '?')) != NULL) { char *tmp3; *tmp++ = '\0'; tmp2 = tmp; while ((tmp = strchr(tmp, '&')) != NULL) { *tmp++ = '\0'; if ((tmp3 = strchr(tmp2, '=')) != NULL) { *tmp3++ = '\0'; /* tmp2 = key, tmp3 = val */ if (strcmp("user", tmp2) == 0) { free(mid->username); mid->username = strdup(tmp3); } else if (strcmp("lang", tmp2) == 0) { free(mid->language); mid->language = strdup(tmp3); } else goto err; } else goto err; tmp2 = tmp; } tmp = tmp2; if ((tmp3 = strchr(tmp2, '=')) != NULL) { *tmp3++ = '\0'; /* tmp2 = key, tmp3 = val */ if (strcmp("user", tmp2) == 0) { free(mid->username); mid->username = strdup(tmp3); } else if (strcmp("lang", tmp2) == 0) { free(mid->language); mid->language = strdup(tmp3); } else goto err; } else goto err; } /* no optional arguments (weird) */ mid->redircnt++; mapi_close_handle(hdl); /* reconnect using the new values */ return(mapi_reconnect(mid));err: tmp2 = alloca(sizeof(char) * (strlen(tmp) + 50)); sprintf(tmp2, "error while parsing redirect: %s", tmp); mapi_close_handle(hdl); mapi_setError(mid, tmp2, "mapi_start_talking", MERROR); return(mid->error); } } } } if (motdlen > 0) { mid->motd = malloc(motdlen + 1); *mid->motd = 0; for (i = 0; i < result->cache.writer; i++) if (result->cache.line[i].rows && result->cache.line[i].rows[0] == '#') { strcat(mid->motd, result->cache.line[i].rows); strcat(mid->motd, "\n"); } } mid->versionId = strcmp("Mserver 5.0", result->cache.line[0].rows) == 0 ? 5 : 4; } mapi_close_handle(hdl); if (mid->trace == MAPI_TRACE) printf("connection established\n"); if (mid->languageId == LANG_MAL) return mid->error; /* tell server about cachelimit */ mapi_cache_limit(mid, mid->cachelimit); return mid->error;}MapiMsgmapi_reconnect(Mapi mid){ MapiMsg rc; rc = connect_to_server(mid); if (rc == MOK) rc = mapi_start_talking(mid); return rc;}/* Create a connection handle and connect to the server using the specified parameters. */Mapimapi_connect(const char *host, int port, const char *username, const char *password, const char *lang, const char *dbname){ Mapi mid; mid = mapi_mapi(host, port, username, password, lang, dbname); if (mid && mid->error == MOK) mapi_reconnect(mid); /* actually, initial connect */ return mid;}/* Create a connection handle and connect to the server using the specified parameters. Use SSL (Secure Socket Layer) to encrypt all data transfers with the server. */Mapimapi_connect_ssl(const char *host, int port, const char *username, const char *password, const char *lang, const char *dbname){ Mapi mid = mapi_mapi(host, port, username, password, lang, dbname);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -