Mono.Zeroconf – Blame information for rev 1

Subversion Repositories:
Rev:
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