#error "Unknown byte order"
#endif
+struct partial_reply {
+ uint16_t len;
+ uint16_t received;
+ unsigned char buf[];
+};
+
struct server_data {
char *interface;
char *domain;
guint timeout;
gboolean enabled;
gboolean connected;
+ struct partial_reply *incoming_reply;
};
struct request_data {
if (server->protocol == IPPROTO_UDP)
connman_info("Removing DNS server %s", server->server);
+ g_free(server->incoming_reply);
g_free(server->server);
g_free(server->domain);
g_free(server->interface);
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
GSList *list;
-
+ hangup:
DBG("TCP server channel closed");
+ /*
+ * Discard any partial response which is buffered; better
+ * to get a proper response from a working server.
+ */
+ g_free(server->incoming_reply);
+ server->incoming_reply = NULL;
+
for (list = request_list; list; list = list->next) {
struct request_data *req = list->data;
struct domain_hdr *hdr;
}
} else if (condition & G_IO_IN) {
- int len, bytes_recv, total_bytes_recv;
- unsigned char reply_len_buf[2];
- uint16_t reply_len;
- unsigned char *reply;
+ struct partial_reply *reply = server->incoming_reply;
+ int bytes_recv;
- len = recv(sk, reply_len_buf, 2, 0);
- if (len < 2)
- return TRUE;
+ if (!reply) {
+ unsigned char reply_len_buf[2];
+ uint16_t reply_len;
+
+ bytes_recv = recv(sk, reply_len_buf, 2, MSG_PEEK);
+ if (!bytes_recv) {
+ goto hangup;
+ } else if (bytes_recv < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return TRUE;
+
+ connman_error("DNS proxy error %s",
+ strerror(errno));
+ goto hangup;
+ } else if (bytes_recv < 2)
+ return TRUE;
- reply_len = reply_len_buf[1] | reply_len_buf[0] << 8;
+ reply_len = reply_len_buf[1] | reply_len_buf[0] << 8;
+ reply_len += 2;
- DBG("TCP reply %d bytes", reply_len);
+ DBG("TCP reply %d bytes", reply_len);
- reply = g_try_malloc0(reply_len + 2);
- if (reply == NULL)
- return TRUE;
+ reply = g_try_malloc(sizeof(*reply) + reply_len + 2);
+ if (!reply)
+ return TRUE;
+
+ reply->len = reply_len;
+ reply->received = 0;
- reply[0] = reply_len_buf[0];
- reply[1] = reply_len_buf[1];
+ server->incoming_reply = reply;
+ }
- total_bytes_recv = bytes_recv = 0;
- while (total_bytes_recv < reply_len) {
- bytes_recv = recv(sk, reply + 2 + total_bytes_recv,
- reply_len - total_bytes_recv, 0);
- if (bytes_recv < 0) {
+ while (reply->received < reply->len) {
+ bytes_recv = recv(sk, reply->buf + reply->received,
+ reply->len - reply->received, 0);
+ if (!bytes_recv) {
+ connman_error("DNS proxy TCP disconnect");
+ break;
+ } else if (bytes_recv < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
- continue;
+ return TRUE;
connman_error("DNS proxy error %s",
- strerror(errno));
-
+ strerror(errno));
break;
}
-
- total_bytes_recv += bytes_recv;
+ reply->received += bytes_recv;
}
- if (total_bytes_recv > reply_len)
- total_bytes_recv = reply_len;
-
- forward_dns_reply(reply, total_bytes_recv + 2, IPPROTO_TCP);
+ forward_dns_reply(reply->buf, reply->received, IPPROTO_TCP);
g_free(reply);
+ server->incoming_reply = NULL;
destroy_server(server);