Mono.Zeroconf – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | // |
2 | // ZeroconfClient.cs |
||
3 | // |
||
4 | // Authors: |
||
5 | // Aaron Bockover <abockover@novell.com> |
||
6 | // |
||
7 | // Copyright (C) 2006-2008 Novell, Inc (http://www.novell.com) |
||
8 | // |
||
9 | // Permission is hereby granted, free of charge, to any person obtaining |
||
10 | // a copy of this software and associated documentation files (the |
||
11 | // "Software"), to deal in the Software without restriction, including |
||
12 | // without limitation the rights to use, copy, modify, merge, publish, |
||
13 | // distribute, sublicense, and/or sell copies of the Software, and to |
||
14 | // permit persons to whom the Software is furnished to do so, subject to |
||
15 | // the following conditions: |
||
16 | // |
||
17 | // The above copyright notice and this permission notice shall be |
||
18 | // included in all copies or substantial portions of the Software. |
||
19 | // |
||
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||
22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
||
24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
||
25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
||
26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||
27 | // |
||
28 | |||
29 | using System; |
||
30 | using System.Collections; |
||
31 | using System.Text.RegularExpressions; |
||
32 | |||
33 | using Mono.Zeroconf; |
||
34 | |||
35 | public class MZClient |
||
36 | { |
||
37 | private static bool resolve_shares = false; |
||
38 | private static uint @interface = 0; |
||
39 | private static AddressProtocol address_protocol = AddressProtocol.Any; |
||
40 | private static string domain = "local"; |
||
41 | private static string app_name = "mzclient"; |
||
42 | private static bool verbose = false; |
||
43 | |||
44 | public static int Main(string [] args) |
||
45 | { |
||
46 | string type = "_workstation._tcp"; |
||
47 | bool show_help = false; |
||
48 | ArrayList services = new ArrayList(); |
||
49 | |||
50 | for(int i = 0; i < args.Length; i++) { |
||
51 | if(args[i][0] != '-') { |
||
52 | continue; |
||
53 | } |
||
54 | |||
55 | switch(args[i]) { |
||
56 | case "-t": |
||
57 | case "--type": |
||
58 | type = args[++i]; |
||
59 | break; |
||
60 | case "-r": |
||
61 | case "--resolve": |
||
62 | resolve_shares = true; |
||
63 | break; |
||
64 | case "-p": |
||
65 | case "--publish": |
||
66 | services.Add(args[++i]); |
||
67 | break; |
||
68 | case "-i": |
||
69 | case "--interface": |
||
70 | if (!UInt32.TryParse (args[++i], out @interface)) { |
||
71 | Console.Error.WriteLine ("Invalid interface index, '{0}'", args[i]); |
||
72 | show_help = true; |
||
73 | } |
||
74 | break; |
||
75 | case "-a": |
||
76 | case "--aprotocol": |
||
77 | string proto = args[++i].ToLower ().Trim (); |
||
78 | switch (proto) { |
||
79 | case "ipv4": case "4": address_protocol = AddressProtocol.IPv4; break; |
||
80 | case "ipv6": case "6": address_protocol = AddressProtocol.IPv6; break; |
||
81 | case "any": case "all": address_protocol = AddressProtocol.Any; break; |
||
82 | default: |
||
83 | Console.Error.WriteLine ("Invalid IP Address Protocol, '{0}'", args[i]); |
||
84 | show_help = true; |
||
85 | break; |
||
86 | } |
||
87 | break; |
||
88 | case "-d": |
||
89 | case "--domain": |
||
90 | domain = args[++i]; |
||
91 | break; |
||
92 | case "-h": |
||
93 | case "--help": |
||
94 | show_help = true; |
||
95 | break; |
||
96 | case "-v": |
||
97 | case "--verbose": |
||
98 | verbose = true; |
||
99 | break; |
||
100 | } |
||
101 | } |
||
102 | |||
103 | if(show_help) { |
||
104 | Console.WriteLine("Usage: {0} [-t type] [--resolve] [--publish \"description\"]", app_name); |
||
105 | Console.WriteLine(); |
||
106 | Console.WriteLine(" -h|--help shows this help"); |
||
107 | Console.WriteLine(" -v|--verbose print verbose details of what's happening"); |
||
108 | Console.WriteLine(" -t|--type uses 'type' as the service type"); |
||
109 | Console.WriteLine(" (default is '_workstation._tcp')"); |
||
110 | Console.WriteLine(" -r|--resolve resolve found services to hosts"); |
||
111 | Console.WriteLine(" -d|--domain which domain to broadcast/listen on"); |
||
112 | Console.WriteLine(" -i|--interface which network interface index to listen"); |
||
113 | Console.WriteLine(" on (default is '0', meaning 'all')"); |
||
114 | Console.WriteLine(" -a|--aprotocol which address protocol to use (Any, IPv4, IPv6)"); |
||
115 | Console.WriteLine(" -p|--publish publish a service of 'description'"); |
||
116 | Console.WriteLine(); |
||
117 | Console.WriteLine("The -d, -i and -a options are optional. By default {0} will listen", app_name); |
||
118 | Console.WriteLine("on all network interfaces ('0') on the 'local' domain, and will resolve "); |
||
119 | Console.WriteLine("all address types, IPv4 and IPv6, as available."); |
||
120 | Console.WriteLine(); |
||
121 | Console.WriteLine("The service description for publishing has the following syntax."); |
||
122 | Console.WriteLine("The TXT record is optional.\n"); |
||
123 | Console.WriteLine(" <type> <port> <name> TXT [ <key>='<value>', ... ]\n"); |
||
124 | Console.WriteLine("For example:\n"); |
||
125 | Console.WriteLine(" -p \"_http._tcp 80 Simple Web Server\""); |
||
126 | Console.WriteLine(" -p \"_daap._tcp 3689 Aaron's Music TXT [ Password='false', \\"); |
||
127 | Console.WriteLine(" Machine Name='Aaron\\'s Box', txtvers='1' ]\""); |
||
128 | Console.WriteLine(); |
||
129 | return 1; |
||
130 | } |
||
131 | |||
132 | if(services.Count > 0) { |
||
133 | foreach(string service_description in services) { |
||
134 | RegisterService(service_description); |
||
135 | } |
||
136 | } else { |
||
137 | if (verbose) { |
||
138 | Console.WriteLine ("Creating a ServiceBrowser with the following settings:"); |
||
139 | Console.WriteLine (" Interface = {0}", @interface == 0 ? "0 (All)" : @interface.ToString ()); |
||
140 | Console.WriteLine (" Address Protocol = {0}", address_protocol); |
||
141 | Console.WriteLine (" Domain = {0}", domain); |
||
142 | Console.WriteLine (" Registration Type = {0}", type); |
||
143 | Console.WriteLine (" Resolve Shares = {0}", resolve_shares); |
||
144 | Console.WriteLine (); |
||
145 | } |
||
146 | |||
147 | Console.WriteLine("Hit ^C when you're bored waiting for responses."); |
||
148 | Console.WriteLine(); |
||
149 | |||
150 | // Listen for events of some service type |
||
151 | ServiceBrowser browser = new ServiceBrowser(); |
||
152 | browser.ServiceAdded += OnServiceAdded; |
||
153 | browser.ServiceRemoved += OnServiceRemoved; |
||
154 | browser.Browse (@interface, address_protocol, type, domain); |
||
155 | } |
||
156 | |||
157 | while(true) { |
||
158 | System.Threading.Thread.Sleep(1000); |
||
159 | } |
||
160 | } |
||
161 | |||
162 | private static void RegisterService(string serviceDescription) |
||
163 | { |
||
164 | Match match = Regex.Match(serviceDescription, @"(_[a-z]+._tcp|udp)\s*(\d+)\s*(.*)"); |
||
165 | if(match.Groups.Count < 4) { |
||
166 | throw new ApplicationException("Invalid service description syntax"); |
||
167 | } |
||
168 | |||
169 | string type = match.Groups[1].Value.Trim(); |
||
170 | short port = Convert.ToInt16(match.Groups[2].Value); |
||
171 | string name = match.Groups[3].Value.Trim(); |
||
172 | |||
173 | int txt_pos = name.IndexOf("TXT"); |
||
174 | string txt_data = null; |
||
175 | |||
176 | if(txt_pos > 0) { |
||
177 | txt_data = name.Substring(txt_pos).Trim(); |
||
178 | name = name.Substring(0, txt_pos).Trim(); |
||
179 | |||
180 | if(txt_data == String.Empty) { |
||
181 | txt_data = null; |
||
182 | } |
||
183 | } |
||
184 | |||
185 | RegisterService service = new RegisterService(); |
||
186 | service.Name = name; |
||
187 | service.RegType = type; |
||
188 | service.ReplyDomain = "local."; |
||
189 | service.Port = port; |
||
190 | |||
191 | TxtRecord record = null; |
||
192 | |||
193 | if(txt_data != null) { |
||
194 | Match tmatch = Regex.Match(txt_data, @"TXT\s*\[(.*)\]"); |
||
195 | |||
196 | if(tmatch.Groups.Count != 2) { |
||
197 | throw new ApplicationException("Invalid TXT record definition syntax"); |
||
198 | } |
||
199 | |||
200 | txt_data = tmatch.Groups[1].Value; |
||
201 | |||
202 | foreach(string part in Regex.Split(txt_data, @"'\s*,")) { |
||
203 | string expr = part.Trim(); |
||
204 | if(!expr.EndsWith("'")) { |
||
205 | expr += "'"; |
||
206 | } |
||
207 | |||
208 | Match pmatch = Regex.Match(expr, @"(\w+\s*\w*)\s*=\s*['](.*)[']\s*"); |
||
209 | string key = pmatch.Groups[1].Value.Trim(); |
||
210 | string val = pmatch.Groups[2].Value.Trim(); |
||
211 | |||
212 | if(key == null || key == String.Empty || val == null || val == String.Empty) { |
||
213 | throw new ApplicationException("Invalid key = 'value' syntax for TXT record item"); |
||
214 | } |
||
215 | |||
216 | if(record == null) { |
||
217 | record = new TxtRecord(); |
||
218 | } |
||
219 | |||
220 | record.Add(key, val); |
||
221 | } |
||
222 | } |
||
223 | |||
224 | if(record != null) { |
||
225 | service.TxtRecord = record; |
||
226 | } |
||
227 | |||
228 | Console.WriteLine("*** Registering name = '{0}', type = '{1}', domain = '{2}'", |
||
229 | service.Name, |
||
230 | service.RegType, |
||
231 | service.ReplyDomain); |
||
232 | |||
233 | service.Response += OnRegisterServiceResponse; |
||
234 | service.Register(); |
||
235 | } |
||
236 | |||
237 | private static void OnServiceAdded(object o, ServiceBrowseEventArgs args) |
||
238 | { |
||
239 | Console.WriteLine("*** Found name = '{0}', type = '{1}', domain = '{2}'", |
||
240 | args.Service.Name, |
||
241 | args.Service.RegType, |
||
242 | args.Service.ReplyDomain); |
||
243 | |||
244 | if(resolve_shares) { |
||
245 | args.Service.Resolved += OnServiceResolved; |
||
246 | args.Service.Resolve(); |
||
247 | } |
||
248 | } |
||
249 | |||
250 | private static void OnServiceRemoved(object o, ServiceBrowseEventArgs args) |
||
251 | { |
||
252 | Console.WriteLine("*** Lost name = '{0}', type = '{1}', domain = '{2}'", |
||
253 | args.Service.Name, |
||
254 | args.Service.RegType, |
||
255 | args.Service.ReplyDomain); |
||
256 | } |
||
257 | |||
258 | private static void OnServiceResolved(object o, ServiceResolvedEventArgs args) |
||
259 | { |
||
260 | IResolvableService service = o as IResolvableService; |
||
261 | Console.Write ("*** Resolved name = '{0}', host ip = '{1}', hostname = {2}, port = '{3}', " + |
||
262 | "interface = '{4}', address type = '{5}'", |
||
263 | service.FullName, service.HostEntry.AddressList[0], service.HostEntry.HostName, service.Port, |
||
264 | service.NetworkInterface, service.AddressProtocol); |
||
265 | |||
266 | ITxtRecord record = service.TxtRecord; |
||
267 | int record_count = record != null ? record.Count : 0; |
||
268 | if(record_count > 0) { |
||
269 | Console.Write(", TXT Record = ["); |
||
270 | for(int i = 0, n = record.Count; i < n; i++) { |
||
271 | TxtRecordItem item = record.GetItemAt(i); |
||
272 | Console.Write("{0} = '{1}'", item.Key, item.ValueString); |
||
273 | if(i < n - 1) { |
||
274 | Console.Write(", "); |
||
275 | } |
||
276 | } |
||
277 | Console.WriteLine("]"); |
||
278 | } else { |
||
279 | Console.WriteLine(""); |
||
280 | } |
||
281 | } |
||
282 | |||
283 | private static void OnRegisterServiceResponse(object o, RegisterServiceEventArgs args) |
||
284 | { |
||
285 | switch(args.ServiceError) { |
||
286 | case ServiceErrorCode.NameConflict: |
||
287 | Console.WriteLine("*** Name Collision! '{0}' is already registered", |
||
288 | args.Service.Name); |
||
289 | break; |
||
290 | case ServiceErrorCode.None: |
||
291 | Console.WriteLine("*** Registered name = '{0}'", args.Service.Name); |
||
292 | break; |
||
293 | case ServiceErrorCode.Unknown: |
||
294 | Console.WriteLine("*** Error registering name = '{0}'", args.Service.Name); |
||
295 | break; |
||
296 | } |
||
297 | } |
||
298 | } |
||
299 |