+// write len bytes. return 0 on success, -1 on error
+static int my_write(dnssd_sock_t sd, char *buf, int len)
+ {
+ // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead.
+ //if (send(sd, buf, len, MSG_WAITALL) != len) return -1;
+ while (len)
+ {
+ ssize_t num_written = send(sd, buf, len, 0);
+ if (num_written < 0 || num_written > len) return -1;
+ buf += num_written;
+ len -= num_written;
+ }
+ return 0;
+ }
+
+// read len bytes. return 0 on success, -1 on error
+static int my_read(dnssd_sock_t sd, char *buf, int len)
+ {
+ // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead.
+ //if (recv(sd, buf, len, MSG_WAITALL) != len) return -1;
+ while (len)
+ {
+ ssize_t num_read = recv(sd, buf, len, 0);
+ if ((num_read == 0) || (num_read < 0) || (num_read > len)) return -1;
+ buf += num_read;
+ len -= num_read;
+ }
+ return 0;
+ }
+
+/* create_hdr
+ *
+ * allocate and initialize an ipc message header. value of len should initially be the
+ * length of the data, and is set to the value of the data plus the header. data_start
+ * is set to point to the beginning of the data section. reuse_socket should be non-zero
+ * for calls that can receive an immediate error return value on their primary socket.
+ * if zero, the path to a control socket is appended at the beginning of the message buffer.
+ * data_start is set past this string.
+ */
+
+static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int reuse_socket)
+ {
+ char *msg = NULL;
+ ipc_msg_hdr *hdr;
+ int datalen;
+ char ctrl_path[256];
+
+ if (!reuse_socket)
+ {
+#if defined(USE_TCP_LOOPBACK)
+ *len += 2; // Allocate space for two-byte port number
+#else
+ struct timeval time;
+ if (gettimeofday(&time, NULL) < 0) return NULL;
+ sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(),
+ (unsigned long)(time.tv_sec & 0xFFF), (unsigned long)(time.tv_usec));
+ *len += strlen(ctrl_path) + 1;
+#endif
+ }
+
+ datalen = (int) *len;
+ *len += sizeof(ipc_msg_hdr);
+
+ // write message to buffer
+ msg = malloc(*len);
+ if (!msg) return NULL;
+
+ bzero(msg, *len);
+ hdr = (void *)msg;
+ hdr->datalen = datalen;
+ hdr->version = VERSION;
+ hdr->op = op;
+ if (reuse_socket) hdr->flags |= IPC_FLAGS_REUSE_SOCKET;
+ *data_start = msg + sizeof(ipc_msg_hdr);
+#if defined(USE_TCP_LOOPBACK)
+ // Put dummy data in for the port, since we don't know what
+ // it is yet. The data will get filled in before we
+ // send the message. This happens in deliver_request().
+ if (!reuse_socket) put_short(0, data_start);
+#else
+ if (!reuse_socket) put_string(ctrl_path, data_start);
+#endif
+ return hdr;
+ }
+
+ // return a connected service ref (deallocate with DNSServiceRefDeallocate)
+static DNSServiceRef connect_to_server(void)
+ {
+ dnssd_sockaddr_t saddr;
+ DNSServiceRef sdr;
+ int NumTries = 0;
+
+#if defined(_WIN32)
+ if (!g_initWinsock)
+ {
+ WSADATA wsaData;
+ DNSServiceErrorType err;
+
+ g_initWinsock = 1;
+
+ err = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
+
+ if (err != 0) return NULL;
+ }
+#endif
+
+ sdr = malloc(sizeof(_DNSServiceRef_t));
+ if (!sdr) return(NULL);
+ sdr->sockfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ if (sdr->sockfd == dnssd_InvalidSocket) { free(sdr); return NULL; }
+#if defined(USE_TCP_LOOPBACK)
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+ saddr.sin_port = htons(MDNS_TCP_SERVERPORT);
+#else
+ saddr.sun_family = AF_LOCAL;
+ strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH);
+#endif
+ while (1)
+ {
+ int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
+ if (!err) break; // If we succeeded, return sdr
+ // If we failed, then it may be because the daemon is still launching.
+ // This can happen for processes that launch early in the boot process, while the
+ // daemon is still coming up. Rather than fail here, we'll wait a bit and try again.
+ // If, after ten seconds, we still can't connect to the daemon,
+ // then we give up and return a failure code.
+ if (++NumTries < 10)
+ sleep(1); // Sleep a bit, then try again
+ else
+ {
+ dnssd_close(sdr->sockfd);
+ sdr->sockfd = dnssd_InvalidSocket;
+ free(sdr);
+ return NULL;
+ }
+ }
+ return sdr;
+ }
+
+static DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd)
+ {
+ ipc_msg_hdr *hdr = msg;
+ uint32_t datalen = hdr->datalen;
+ dnssd_sockaddr_t caddr, daddr; // (client and daemon address structs)
+ char *data = (char *)msg + sizeof(ipc_msg_hdr);
+ dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket;
+ int ret;
+ unsigned int len = sizeof(caddr);
+ DNSServiceErrorType err = kDNSServiceErr_Unknown;
+
+ if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown;
+
+ if (!reuse_sd)
+ {
+ // setup temporary error socket
+ if ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) < 0)
+ goto cleanup;
+ bzero(&caddr, sizeof(caddr));
+
+#if defined(USE_TCP_LOOPBACK)
+ {
+ union { uint16_t s; u_char b[2]; } port;
+ caddr.sin_family = AF_INET;
+ caddr.sin_port = 0;
+ caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+ ret = bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr));
+ if (ret < 0) goto cleanup;
+ if (getsockname(listenfd, (struct sockaddr*) &caddr, &len) < 0) goto cleanup;
+ listen(listenfd, 1);
+ port.s = caddr.sin_port;
+ data[0] = port.b[0]; // don't switch the byte order, as the
+ data[1] = port.b[1]; // daemon expects it in network byte order
+ }
+#else
+ {
+ mode_t mask = umask(0);
+ caddr.sun_family = AF_LOCAL;
+#ifndef NOT_HAVE_SA_LEN // According to Stevens (section 3.2), there is no portable way to
+ // determine whether sa_len is defined on a particular platform.
+ caddr.sun_len = sizeof(struct sockaddr_un);
+#endif
+ strcpy(caddr.sun_path, data);
+ ret = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr));
+ umask(mask);
+ if (ret < 0) goto cleanup;
+ listen(listenfd, 1);
+ }
+#endif
+ }
+
+ ConvertHeaderBytes(hdr);
+ if (my_write(sdr->sockfd, msg, datalen + sizeof(ipc_msg_hdr)) < 0)
+ goto cleanup;
+ free(msg);
+ msg = NULL;
+
+ if (reuse_sd) errsd = sdr->sockfd;
+ else
+ {
+ len = sizeof(daddr);
+ errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
+ if (errsd < 0) goto cleanup;
+ }
+
+ if (my_read(errsd, (char*)&err, (int)sizeof(err)) < 0)
+ err = kDNSServiceErr_Unknown;
+ else
+ err = ntohl(err);
+
+cleanup:
+ if (!reuse_sd && listenfd > 0) dnssd_close(listenfd);
+ if (!reuse_sd && errsd > 0) dnssd_close(errsd);
+#if !defined(USE_TCP_LOOPBACK)
+ if (!reuse_sd && data) unlink(data);
+#endif
+ if (msg) free(msg);
+ return err;
+ }
+
+int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef)