Mercurial > pidgin.yaz
comparison libpurple/protocols/bonjour/mdns_win32.c @ 17733:d7b50cac1c7a
This is a patch from Chris Davies to make Bonjour work on Windows using the Apple Bonjour framework. It turns out that the actual DNS-SD library is (3 clause) BSD licensed, so we can use it.
There are a few changes by me, mainly to fix the howl implementation.
Fixes #1117 .
There appear to be a few bugs, but I believe that they were also present previously. I'm hoping to do some more tweaking before the next release.
The howl implementation will eventually be supersceded by a native avahi implementation, so I opted for a somewhat dirty hack to enable it instead of doing something with config.h.
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Tue, 05 Jun 2007 03:38:22 +0000 |
parents | |
children | c96b085ddf5c |
comparison
equal
deleted
inserted
replaced
17726:7e856734b712 | 17733:d7b50cac1c7a |
---|---|
1 /* | |
2 * This program is free software; you can redistribute it and/or modify | |
3 * it under the terms of the GNU General Public License as published by | |
4 * the Free Software Foundation; either version 2 of the License, or | |
5 * (at your option) any later version. | |
6 * | |
7 * This program is distributed in the hope that it will be useful, | |
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 * GNU Library General Public License for more details. | |
11 * | |
12 * You should have received a copy of the GNU General Public License | |
13 * along with this program; if not, write to the Free Software | |
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
15 */ | |
16 | |
17 #include "mdns_win32.h" | |
18 | |
19 #include "debug.h" | |
20 | |
21 void | |
22 _mdns_resolve_host_callback(GSList *hosts, gpointer data, const char *error_message) | |
23 { | |
24 ResolveCallbackArgs* args = (ResolveCallbackArgs*)data; | |
25 | |
26 if (!hosts || !hosts->data) | |
27 { | |
28 purple_debug_error("bonjour", "host resolution - callback error.\n"); | |
29 } | |
30 else | |
31 { | |
32 struct sockaddr_in *addr = (struct sockaddr_in*)g_slist_nth_data(hosts, 1); | |
33 BonjourBuddy* buddy = args->buddy; | |
34 | |
35 buddy->ip = inet_ntoa(addr->sin_addr); | |
36 | |
37 /* finally, set up the continuous txt record watcher, and add the buddy to purple */ | |
38 | |
39 if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&buddy->txt_query, 0, 0, args->fqn, | |
40 kDNSServiceType_TXT, kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy)) | |
41 { | |
42 gint fd = DNSServiceRefSockFD(buddy->txt_query); | |
43 buddy->txt_query_fd = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, buddy->txt_query); | |
44 | |
45 bonjour_buddy_add_to_purple(buddy); | |
46 } | |
47 else | |
48 { | |
49 bonjour_buddy_delete(buddy); | |
50 } | |
51 | |
52 } | |
53 | |
54 /* free the hosts list*/ | |
55 g_slist_free(hosts); | |
56 | |
57 /* free the remaining args memory */ | |
58 purple_dnsquery_destroy(args->query); | |
59 g_free(args->fqn); | |
60 free(args); | |
61 } | |
62 | |
63 void DNSSD_API | |
64 _mdns_text_record_query_callback(DNSServiceRef DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, | |
65 DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, | |
66 const void *rdata, uint32_t ttl, void *context) | |
67 { | |
68 if (kDNSServiceErr_NoError != errorCode) | |
69 { | |
70 purple_debug_error("bonjour", "text record query - callback error.\n"); | |
71 } | |
72 else if (flags & kDNSServiceFlagsAdd) | |
73 { | |
74 BonjourBuddy *buddy = (BonjourBuddy*)context; | |
75 _mdns_parse_text_record(buddy, rdata, rdlen); | |
76 bonjour_buddy_add_to_purple(buddy); | |
77 } | |
78 } | |
79 | |
80 void DNSSD_API | |
81 _mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, | |
82 const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context) | |
83 { | |
84 ResolveCallbackArgs *args = (ResolveCallbackArgs*)context; | |
85 | |
86 /* remove the input fd and destroy the service ref */ | |
87 purple_input_remove(args->resolver_fd); | |
88 DNSServiceRefDeallocate(args->resolver); | |
89 | |
90 if (kDNSServiceErr_NoError != errorCode) | |
91 { | |
92 purple_debug_error("bonjour", "service resolver - callback error.\n"); | |
93 bonjour_buddy_delete(args->buddy); | |
94 free(args); | |
95 } | |
96 else | |
97 { | |
98 args->buddy->port_p2pj = port; | |
99 | |
100 /* parse the text record */ | |
101 _mdns_parse_text_record(args->buddy, txtRecord, txtLen); | |
102 | |
103 /* set more arguments, and start the host resolver */ | |
104 args->fqn = g_strdup(fullname); | |
105 | |
106 if (NULL == (args->query = | |
107 purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args))) | |
108 { | |
109 purple_debug_error("bonjour", "service resolver - host resolution failed.\n"); | |
110 bonjour_buddy_delete(args->buddy); | |
111 g_free(args->fqn); | |
112 free(args); | |
113 } | |
114 } | |
115 | |
116 } | |
117 | |
118 void DNSSD_API | |
119 _mdns_service_register_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, | |
120 const char *name, const char *regtype, const char *domain, void *context) | |
121 { | |
122 /* we don't actually care about anything said in this callback - this is only here because Bonjour for windows is broken */ | |
123 if (kDNSServiceErr_NoError != errorCode) | |
124 { | |
125 purple_debug_error("bonjour", "service advertisement - callback error.\n"); | |
126 } | |
127 else | |
128 { | |
129 purple_debug_info("bonjour", "service advertisement - callback.\n"); | |
130 } | |
131 } | |
132 | |
133 void DNSSD_API | |
134 _mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, | |
135 DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) | |
136 { | |
137 PurpleAccount *account = (PurpleAccount*)context; | |
138 PurpleBuddy *gb = NULL; | |
139 | |
140 if (kDNSServiceErr_NoError != errorCode) | |
141 { | |
142 purple_debug_error("bonjour", "service browser - callback error"); | |
143 } | |
144 else if (flags & kDNSServiceFlagsAdd) | |
145 { | |
146 /* A presence service instance has been discovered... check it isn't us! */ | |
147 if (0 != g_ascii_strcasecmp(serviceName, account->username)) | |
148 { | |
149 /* OK, lets go ahead and resolve it to add to the buddy list */ | |
150 ResolveCallbackArgs *args = malloc(sizeof(ResolveCallbackArgs)); | |
151 args->buddy = bonjour_buddy_new(serviceName, account); | |
152 | |
153 if (kDNSServiceErr_NoError != DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype, replyDomain, _mdns_service_resolve_callback, args)) | |
154 { | |
155 bonjour_buddy_delete(args->buddy); | |
156 free(args); | |
157 purple_debug_error("bonjour", "service browser - failed to resolve service.\n"); | |
158 } | |
159 else | |
160 { | |
161 /* get a file descriptor for this service ref, and add it to the input list */ | |
162 gint resolver_fd = DNSServiceRefSockFD(args->resolver); | |
163 args->resolver_fd = purple_input_add(resolver_fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver); | |
164 } | |
165 } | |
166 } | |
167 else | |
168 { | |
169 /* A peer has sent a goodbye packet, remove them from the buddy list */ | |
170 purple_debug_info("bonjour", "service browser - remove notification\n"); | |
171 gb = purple_find_buddy(account, serviceName); | |
172 if (gb != NULL) | |
173 { | |
174 bonjour_buddy_delete(gb->proto_data); | |
175 purple_blist_remove_buddy(gb); | |
176 } | |
177 } | |
178 } | |
179 | |
180 void | |
181 _mdns_parse_text_record(BonjourBuddy* buddy, const char* record, uint16_t record_len) | |
182 { | |
183 char *txt_entry; | |
184 uint8_t txt_len; | |
185 | |
186 if (NULL != (txt_entry = (char*)TXTRecordGetValuePtr(record_len, record, "1st", &txt_len))) | |
187 { | |
188 set_bonjour_buddy_value(buddy, E_BUDDY_FIRST, txt_entry, txt_len); | |
189 } | |
190 | |
191 if (NULL != (txt_entry = (char*)TXTRecordGetValuePtr(record_len, record, "last", &txt_len))) | |
192 { | |
193 set_bonjour_buddy_value(buddy, E_BUDDY_LAST, txt_entry, txt_len); | |
194 } | |
195 | |
196 if (NULL != (txt_entry = (char*)TXTRecordGetValuePtr(record_len, record, "status", &txt_len))) | |
197 { | |
198 set_bonjour_buddy_value(buddy, E_BUDDY_STATUS, txt_entry, txt_len); | |
199 } | |
200 | |
201 if (NULL != (txt_entry = (char*)TXTRecordGetValuePtr(record_len, record, "email", &txt_len))) | |
202 { | |
203 set_bonjour_buddy_value(buddy, E_BUDDY_EMAIL, txt_entry, txt_len); | |
204 } | |
205 | |
206 if (NULL != (txt_entry = (char*)TXTRecordGetValuePtr(record_len, record, "jid", &txt_len))) | |
207 { | |
208 set_bonjour_buddy_value(buddy, E_BUDDY_JID, txt_entry, txt_len); | |
209 } | |
210 | |
211 if (NULL != (txt_entry = (char*)TXTRecordGetValuePtr(record_len, record, "AIM", &txt_len))) | |
212 { | |
213 set_bonjour_buddy_value(buddy, E_BUDDY_AIM, txt_entry, txt_len); | |
214 } | |
215 | |
216 if (NULL != (txt_entry = (char*)TXTRecordGetValuePtr(record_len, record, "VC", &txt_len))) | |
217 { | |
218 set_bonjour_buddy_value(buddy, E_BUDDY_VC, txt_entry, txt_len); | |
219 } | |
220 | |
221 if (NULL != (txt_entry = (char*)TXTRecordGetValuePtr(record_len, record, "phsh", &txt_len))) | |
222 { | |
223 set_bonjour_buddy_value(buddy, E_BUDDY_PHSH, txt_entry, txt_len); | |
224 } | |
225 | |
226 if (NULL != (txt_entry = (char*)TXTRecordGetValuePtr(record_len, record, "msg", &txt_len))) | |
227 { | |
228 set_bonjour_buddy_value(buddy, E_BUDDY_MSG, txt_entry, txt_len); | |
229 } | |
230 } | |
231 | |
232 int | |
233 _mdns_publish(BonjourDnsSd *data, PublishType type) | |
234 { | |
235 TXTRecordRef dns_data; | |
236 char portstring[6]; | |
237 int ret = 0; | |
238 | |
239 TXTRecordCreate(&dns_data, 256, NULL); | |
240 | |
241 /* Convert the port to a string */ | |
242 snprintf(portstring, sizeof(portstring), "%d", data->port_p2pj); | |
243 | |
244 /* Publish standard records */ | |
245 | |
246 if (kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "txtvers", strlen(data->txtvers), data->txtvers) || | |
247 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "version", strlen(data->version), data->version) || | |
248 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "1st", strlen(data->first), data->first) || | |
249 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "last", strlen(data->last), data->last) || | |
250 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "port", strlen(portstring), portstring) || | |
251 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "phsh", strlen(data->phsh), data->phsh) || | |
252 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "status", strlen(data->status), data->status) || | |
253 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "vc", strlen(data->vc), data->vc)) | |
254 { | |
255 purple_debug_error("bonjour", "Unable to allocate memory for text record.\n"); | |
256 ret = -1; | |
257 } | |
258 else if ((data->email != NULL) && (*data->email != '\0') && | |
259 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "email", strlen(data->email), data->email)) | |
260 { | |
261 purple_debug_error("bonjour", "Unable to allocate memory for text record.\n"); | |
262 ret = -1; | |
263 } | |
264 else if ((data->jid != NULL) && (*data->jid != '\0') && | |
265 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "email", strlen(data->jid), data->jid)) | |
266 { | |
267 purple_debug_error("bonjour", "Unable to allocate memory for text record.\n"); | |
268 ret = -1; | |
269 } | |
270 else if ((data->AIM != NULL) && (*data->AIM != '\0') && | |
271 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "AIM", strlen(data->AIM), data->AIM)) | |
272 { | |
273 purple_debug_error("bonjour", "Unable to allocate memory for text record.\n"); | |
274 ret = -1; | |
275 } | |
276 else if ((data->msg != NULL) && (*data->msg != '\0') && | |
277 kDNSServiceErr_NoError != TXTRecordSetValue(&dns_data, "msg", strlen(data->msg), data->msg)) | |
278 { | |
279 purple_debug_error("bonjour", "Unable to allocate memory for text record.\n"); | |
280 ret = -1; | |
281 } | |
282 else | |
283 { | |
284 DNSServiceErrorType err = kDNSServiceErr_NoError; | |
285 | |
286 /* OK, we're done constructing the text record, (re)publish the service */ | |
287 | |
288 switch (type) | |
289 { | |
290 case PUBLISH_START: | |
291 err = DNSServiceRegister(&data->advertisement, 0, 0, data->name, ICHAT_SERVICE, | |
292 NULL, NULL, data->port_p2pj, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), | |
293 _mdns_service_register_callback, NULL); | |
294 break; | |
295 | |
296 case PUBLISH_UPDATE: | |
297 err = DNSServiceUpdateRecord(data->advertisement, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0); | |
298 break; | |
299 } | |
300 | |
301 if (kDNSServiceErr_NoError != err) | |
302 { | |
303 purple_debug_error("bonjour", "Failed to publish presence service.\n"); | |
304 ret = -1; | |
305 } | |
306 else if (PUBLISH_START == type) | |
307 { | |
308 /* hack: Bonjour on windows is broken. We don't care about the callback but we have to listen anyway */ | |
309 gint advertisement_fd = DNSServiceRefSockFD(data->advertisement); | |
310 data->advertisement_fd = purple_input_add(advertisement_fd, PURPLE_INPUT_READ, _mdns_handle_event, data->advertisement); | |
311 } | |
312 } | |
313 | |
314 /* Free the memory used by temp data */ | |
315 TXTRecordDeallocate(&dns_data); | |
316 return ret; | |
317 } | |
318 | |
319 void | |
320 _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) | |
321 { | |
322 DNSServiceProcessResult((DNSServiceRef)data); | |
323 } |