changeset 72 | 53f15893a1a7 |
parent 66 | 4558994ab3b3 |
child 74 | e619e18dcec3 |
child 98 | c9cc4eda6dce |
71:578c2a83dc18 | 72:53f15893a1a7 |
---|---|
45 if cl.socket == nil { |
45 if cl.socket == nil { |
46 cl.waitForSocket() |
46 cl.waitForSocket() |
47 } |
47 } |
48 nr, err := cl.socket.Read(p) |
48 nr, err := cl.socket.Read(p) |
49 if nr == 0 { |
49 if nr == 0 { |
50 if errno, ok := err.(*net.OpError) ; ok { |
50 if errno, ok := err.(*net.OpError); ok { |
51 if errno.Timeout() { |
51 if errno.Timeout() { |
52 continue |
52 continue |
53 } |
53 } |
54 } |
54 } |
55 if Log != nil { |
55 if Log != nil { |
87 } |
87 } |
88 } |
88 } |
89 } |
89 } |
90 |
90 |
91 func readXml(r io.Reader, ch chan<- interface{}, |
91 func readXml(r io.Reader, ch chan<- interface{}, |
92 extStanza map[string] func(*xml.Name) interface{}) { |
92 extStanza map[string]func(*xml.Name) interface{}) { |
93 if Loglevel >= syslog.LOG_DEBUG { |
93 if Loglevel >= syslog.LOG_DEBUG { |
94 pr, pw := io.Pipe() |
94 pr, pw := io.Pipe() |
95 go tee(r, pw, "S: ") |
95 go tee(r, pw, "S: ") |
96 r = pr |
96 r = pr |
97 } |
97 } |
110 } |
110 } |
111 break |
111 break |
112 } |
112 } |
113 var se xml.StartElement |
113 var se xml.StartElement |
114 var ok bool |
114 var ok bool |
115 if se, ok = t.(xml.StartElement) ; !ok { |
115 if se, ok = t.(xml.StartElement); !ok { |
116 continue |
116 continue |
117 } |
117 } |
118 |
118 |
119 // Allocate the appropriate structure for this token. |
119 // Allocate the appropriate structure for this token. |
120 var obj interface{} |
120 var obj interface{} |
163 } |
163 } |
164 |
164 |
165 // If it's a Stanza, we try to unmarshal its innerxml |
165 // If it's a Stanza, we try to unmarshal its innerxml |
166 // into objects of the appropriate respective |
166 // into objects of the appropriate respective |
167 // types. This is specified by our extensions. |
167 // types. This is specified by our extensions. |
168 if st, ok := obj.(Stanza) ; ok { |
168 if st, ok := obj.(Stanza); ok { |
169 err = parseExtended(st, extStanza) |
169 err = parseExtended(st, extStanza) |
170 if err != nil { |
170 if err != nil { |
171 if Log != nil { |
171 if Log != nil { |
172 Log.Err("ext unmarshal: " + |
172 Log.Err("ext unmarshal: " + |
173 err.String()) |
173 err.String()) |
179 // Put it on the channel. |
179 // Put it on the channel. |
180 ch <- obj |
180 ch <- obj |
181 } |
181 } |
182 } |
182 } |
183 |
183 |
184 func parseExtended(st Stanza, extStanza map[string] func(*xml.Name) interface{}) os.Error { |
184 func parseExtended(st Stanza, extStanza map[string]func(*xml.Name) interface{}) os.Error { |
185 // Now parse the stanza's innerxml to find the string that we |
185 // Now parse the stanza's innerxml to find the string that we |
186 // can unmarshal this nested element from. |
186 // can unmarshal this nested element from. |
187 reader := strings.NewReader(st.innerxml()) |
187 reader := strings.NewReader(st.innerxml()) |
188 p := xml.NewParser(reader) |
188 p := xml.NewParser(reader) |
189 for { |
189 for { |
192 break |
192 break |
193 } |
193 } |
194 if err != nil { |
194 if err != nil { |
195 return err |
195 return err |
196 } |
196 } |
197 if se, ok := t.(xml.StartElement) ; ok { |
197 if se, ok := t.(xml.StartElement); ok { |
198 if con, ok := extStanza[se.Name.Space] ; ok { |
198 if con, ok := extStanza[se.Name.Space]; ok { |
199 // Call the indicated constructor. |
199 // Call the indicated constructor. |
200 nested := con(&se.Name) |
200 nested := con(&se.Name) |
201 |
201 |
202 // Unmarshal the nested element and |
202 // Unmarshal the nested element and |
203 // stuff it back into the stanza. |
203 // stuff it back into the stanza. |
218 pr, pw := io.Pipe() |
218 pr, pw := io.Pipe() |
219 go tee(pr, w, "C: ") |
219 go tee(pr, w, "C: ") |
220 w = pw |
220 w = pw |
221 } |
221 } |
222 defer func(w io.Writer) { |
222 defer func(w io.Writer) { |
223 if c, ok := w.(io.Closer) ; ok { |
223 if c, ok := w.(io.Closer); ok { |
224 c.Close() |
224 c.Close() |
225 } |
225 } |
226 }(w) |
226 }(w) |
227 |
227 |
228 for obj := range ch { |
228 for obj := range ch { |
237 } |
237 } |
238 |
238 |
239 func (cl *Client) readStream(srvIn <-chan interface{}, cliOut chan<- Stanza) { |
239 func (cl *Client) readStream(srvIn <-chan interface{}, cliOut chan<- Stanza) { |
240 defer close(cliOut) |
240 defer close(cliOut) |
241 |
241 |
242 handlers := make(map[string] func(Stanza) bool) |
242 handlers := make(map[string]func(Stanza) bool) |
243 Loop: |
243 Loop: |
244 for { |
244 for { |
245 select { |
245 select { |
246 case h := <- cl.handlers: |
246 case h := <-cl.handlers: |
247 handlers[h.id] = h.f |
247 handlers[h.id] = h.f |
248 case x, ok := <- srvIn: |
248 case x, ok := <-srvIn: |
249 if !ok { |
249 if !ok { |
250 break Loop |
250 break Loop |
251 } |
251 } |
252 send := false |
252 send := false |
253 switch obj := x.(type) { |
253 switch obj := x.(type) { |
290 // This loop is paused until resource binding is complete. Otherwise |
290 // This loop is paused until resource binding is complete. Otherwise |
291 // the app might inject something inappropriate into our negotiations |
291 // the app might inject something inappropriate into our negotiations |
292 // with the server. The control channel controls this loop's |
292 // with the server. The control channel controls this loop's |
293 // activity. |
293 // activity. |
294 func writeStream(srvOut chan<- interface{}, cliIn <-chan Stanza, |
294 func writeStream(srvOut chan<- interface{}, cliIn <-chan Stanza, |
295 control <-chan int) { |
295 control <-chan int) { |
296 defer close(srvOut) |
296 defer close(srvOut) |
297 |
297 |
298 var input <-chan Stanza |
298 var input <-chan Stanza |
299 Loop: |
299 Loop: |
300 for { |
300 for { |
301 select { |
301 select { |
302 case status := <- control: |
302 case status := <-control: |
303 switch status { |
303 switch status { |
304 case 0: |
304 case 0: |
305 input = nil |
305 input = nil |
306 case 1: |
306 case 1: |
307 input = cliIn |
307 input = cliIn |
308 case -1: |
308 case -1: |
309 break Loop |
309 break Loop |
310 } |
310 } |
311 case x, ok := <- input: |
311 case x, ok := <-input: |
312 if !ok { |
312 if !ok { |
313 break Loop |
313 break Loop |
314 } |
314 } |
315 if x == nil { |
315 if x == nil { |
316 if Log != nil { |
316 if Log != nil { |
325 } |
325 } |
326 |
326 |
327 // Stanzas from the remote go up through a stack of filters to the |
327 // Stanzas from the remote go up through a stack of filters to the |
328 // app. This function manages the filters. |
328 // app. This function manages the filters. |
329 func filterTop(filterOut <-chan <-chan Stanza, filterIn chan<- <-chan Stanza, |
329 func filterTop(filterOut <-chan <-chan Stanza, filterIn chan<- <-chan Stanza, |
330 topFilter <-chan Stanza, app chan<- Stanza) { |
330 topFilter <-chan Stanza, app chan<- Stanza) { |
331 defer close(app) |
331 defer close(app) |
332 Loop: |
332 Loop: |
333 for { |
333 for { |
334 select { |
334 select { |
335 case newFilterOut := <- filterOut: |
335 case newFilterOut := <-filterOut: |
336 if newFilterOut == nil { |
336 if newFilterOut == nil { |
337 if Log != nil { |
337 if Log != nil { |
338 Log.Warning("Received nil filter") |
338 Log.Warning("Received nil filter") |
339 } |
339 } |
340 filterIn <- nil |
340 filterIn <- nil |
352 } |
352 } |
353 } |
353 } |
354 |
354 |
355 func filterBottom(from <-chan Stanza, to chan<- Stanza) { |
355 func filterBottom(from <-chan Stanza, to chan<- Stanza) { |
356 defer close(to) |
356 defer close(to) |
357 for data := range(from) { |
357 for data := range from { |
358 to <- data |
358 to <- data |
359 } |
359 } |
360 } |
360 } |
361 |
361 |
362 func handleStream(ss *stream) { |
362 func handleStream(ss *stream) { |
441 } |
441 } |
442 |
442 |
443 // BUG(cjyar): Doesn't implement TLS/SASL EXTERNAL. |
443 // BUG(cjyar): Doesn't implement TLS/SASL EXTERNAL. |
444 func (cl *Client) chooseSasl(fe *Features) { |
444 func (cl *Client) chooseSasl(fe *Features) { |
445 var digestMd5 bool |
445 var digestMd5 bool |
446 for _, m := range(fe.Mechanisms.Mechanism) { |
446 for _, m := range fe.Mechanisms.Mechanism { |
447 switch strings.ToLower(m) { |
447 switch strings.ToLower(m) { |
448 case "digest-md5": |
448 case "digest-md5": |
449 digestMd5 = true |
449 digestMd5 = true |
450 } |
450 } |
451 } |
451 } |
452 |
452 |
453 if digestMd5 { |
453 if digestMd5 { |
454 auth := &auth{XMLName: xml.Name{Space: NsSASL, Local: |
454 auth := &auth{XMLName: xml.Name{Space: NsSASL, Local: "auth"}, Mechanism: "DIGEST-MD5"} |
455 "auth"}, Mechanism: "DIGEST-MD5"} |
|
456 cl.xmlOut <- auth |
455 cl.xmlOut <- auth |
457 } |
456 } |
458 } |
457 } |
459 |
458 |
460 func (cl *Client) handleSasl(srv *auth) { |
459 func (cl *Client) handleSasl(srv *auth) { |
465 if err != nil { |
464 if err != nil { |
466 if Log != nil { |
465 if Log != nil { |
467 Log.Err("SASL challenge decode: " + |
466 Log.Err("SASL challenge decode: " + |
468 err.String()) |
467 err.String()) |
469 } |
468 } |
470 return; |
469 return |
471 } |
470 } |
472 srvMap := parseSasl(string(str)) |
471 srvMap := parseSasl(string(str)) |
473 |
472 |
474 if cl.saslExpected == "" { |
473 if cl.saslExpected == "" { |
475 cl.saslDigest1(srvMap) |
474 cl.saslDigest1(srvMap) |
488 ss := &stream{To: cl.Jid.Domain, Version: Version} |
487 ss := &stream{To: cl.Jid.Domain, Version: Version} |
489 cl.xmlOut <- ss |
488 cl.xmlOut <- ss |
490 } |
489 } |
491 } |
490 } |
492 |
491 |
493 func (cl *Client) saslDigest1(srvMap map[string] string) { |
492 func (cl *Client) saslDigest1(srvMap map[string]string) { |
494 // Make sure it supports qop=auth |
493 // Make sure it supports qop=auth |
495 var hasAuth bool |
494 var hasAuth bool |
496 for _, qop := range(strings.Fields(srvMap["qop"])) { |
495 for _, qop := range strings.Fields(srvMap["qop"]) { |
497 if qop == "auth" { |
496 if qop == "auth" { |
498 hasAuth = true |
497 hasAuth = true |
499 } |
498 } |
500 } |
499 } |
501 if !hasAuth { |
500 if !hasAuth { |
502 if Log != nil { |
501 if Log != nil { |
503 Log.Err("Server doesn't support SASL auth") |
502 Log.Err("Server doesn't support SASL auth") |
504 } |
503 } |
505 return; |
504 return |
506 } |
505 } |
507 |
506 |
508 // Pick a realm. |
507 // Pick a realm. |
509 var realm string |
508 var realm string |
510 if srvMap["realm"] != "" { |
509 if srvMap["realm"] != "" { |
550 clMap := make(map[string]string) |
549 clMap := make(map[string]string) |
551 clMap["realm"] = `"` + realm + `"` |
550 clMap["realm"] = `"` + realm + `"` |
552 clMap["username"] = `"` + username + `"` |
551 clMap["username"] = `"` + username + `"` |
553 clMap["nonce"] = `"` + nonce + `"` |
552 clMap["nonce"] = `"` + nonce + `"` |
554 clMap["cnonce"] = `"` + cnonceStr + `"` |
553 clMap["cnonce"] = `"` + cnonceStr + `"` |
555 clMap["nc"] = nonceCountStr |
554 clMap["nc"] = nonceCountStr |
556 clMap["qop"] = "auth" |
555 clMap["qop"] = "auth" |
557 clMap["digest-uri"] = `"` + digestUri + `"` |
556 clMap["digest-uri"] = `"` + digestUri + `"` |
558 clMap["response"] = response |
557 clMap["response"] = response |
559 if srvMap["charset"] == "utf-8" { |
558 if srvMap["charset"] == "utf-8" { |
560 clMap["charset"] = "utf-8" |
559 clMap["charset"] = "utf-8" |
561 } |
560 } |
562 |
561 |
563 // Encode the map and send it. |
562 // Encode the map and send it. |
564 clStr := packSasl(clMap) |
563 clStr := packSasl(clMap) |
565 b64 := base64.StdEncoding |
564 b64 := base64.StdEncoding |
566 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: |
565 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "response"}, Chardata: b64.EncodeToString([]byte(clStr))} |
567 "response"}, Chardata: |
|
568 b64.EncodeToString([]byte(clStr))} |
|
569 cl.xmlOut <- clObj |
566 cl.xmlOut <- clObj |
570 } |
567 } |
571 |
568 |
572 func (cl *Client) saslDigest2(srvMap map[string] string) { |
569 func (cl *Client) saslDigest2(srvMap map[string]string) { |
573 if cl.saslExpected == srvMap["rspauth"] { |
570 if cl.saslExpected == srvMap["rspauth"] { |
574 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: |
571 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "response"}} |
575 "response"}} |
|
576 cl.xmlOut <- clObj |
572 cl.xmlOut <- clObj |
577 } else { |
573 } else { |
578 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: |
574 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "failure"}, Any: &Generic{XMLName: xml.Name{Space: NsSASL, |
579 "failure"}, Any: |
575 Local: "abort"}}} |
580 &Generic{XMLName: xml.Name{Space: NsSASL, |
|
581 Local: "abort"}}} |
|
582 cl.xmlOut <- clObj |
576 cl.xmlOut <- clObj |
583 } |
577 } |
584 } |
578 } |
585 |
579 |
586 // Takes a string like `key1=value1,key2="value2"...` and returns a |
580 // Takes a string like `key1=value1,key2="value2"...` and returns a |
587 // key/value map. |
581 // key/value map. |
588 func parseSasl(in string) map[string]string { |
582 func parseSasl(in string) map[string]string { |
589 re := regexp.MustCompile(`([^=]+)="?([^",]+)"?,?`) |
583 re := regexp.MustCompile(`([^=]+)="?([^",]+)"?,?`) |
590 strs := re.FindAllStringSubmatch(in, -1) |
584 strs := re.FindAllStringSubmatch(in, -1) |
591 m := make(map[string]string) |
585 m := make(map[string]string) |
592 for _, pair := range(strs) { |
586 for _, pair := range strs { |
593 key := strings.ToLower(string(pair[1])) |
587 key := strings.ToLower(string(pair[1])) |
594 value := string(pair[2]) |
588 value := string(pair[2]) |
595 m[key] = value |
589 m[key] = value |
596 } |
590 } |
597 return m |
591 return m |
598 } |
592 } |
599 |
593 |
600 // Inverse of parseSasl(). |
594 // Inverse of parseSasl(). |
601 func packSasl(m map[string]string) string { |
595 func packSasl(m map[string]string) string { |
602 var terms []string |
596 var terms []string |
603 for key, value := range(m) { |
597 for key, value := range m { |
604 if key == "" || value == "" || value == `""` { |
598 if key == "" || value == "" || value == `""` { |
605 continue |
599 continue |
606 } |
600 } |
607 terms = append(terms, key + "=" + value) |
601 terms = append(terms, key+"="+value) |
608 } |
602 } |
609 return strings.Join(terms, ",") |
603 return strings.Join(terms, ",") |
610 } |
604 } |
611 |
605 |
612 // Computes the response string for digest authentication. |
606 // Computes the response string for digest authentication. |
613 func saslDigestResponse(username, realm, passwd, nonce, cnonceStr, |
607 func saslDigestResponse(username, realm, passwd, nonce, cnonceStr, |
614 authenticate, digestUri, nonceCountStr string) string { |
608 authenticate, digestUri, nonceCountStr string) string { |
615 h := func(text string) []byte { |
609 h := func(text string) []byte { |
616 h := md5.New() |
610 h := md5.New() |
617 h.Write([]byte(text)) |
611 h.Write([]byte(text)) |
618 return h.Sum() |
612 return h.Sum() |
619 } |
613 } |
622 } |
616 } |
623 kd := func(secret, data string) []byte { |
617 kd := func(secret, data string) []byte { |
624 return h(secret + ":" + data) |
618 return h(secret + ":" + data) |
625 } |
619 } |
626 |
620 |
627 a1 := string(h(username + ":" + realm + ":" + passwd)) + ":" + |
621 a1 := string(h(username+":"+realm+":"+passwd)) + ":" + |
628 nonce + ":" + cnonceStr |
622 nonce + ":" + cnonceStr |
629 a2 := authenticate + ":" + digestUri |
623 a2 := authenticate + ":" + digestUri |
630 response := hex(kd(hex(h(a1)), nonce + ":" + |
624 response := hex(kd(hex(h(a1)), nonce+":"+ |
631 nonceCountStr + ":" + cnonceStr + ":auth:" + |
625 nonceCountStr+":"+cnonceStr+":auth:"+ |
632 hex(h(a2)))) |
626 hex(h(a2)))) |
633 return response |
627 return response |
634 } |
628 } |
635 |
629 |
636 // Send a request to bind a resource. RFC 3920, section 7. |
630 // Send a request to bind a resource. RFC 3920, section 7. |
638 res := cl.Jid.Resource |
632 res := cl.Jid.Resource |
639 bindReq := &bindIq{} |
633 bindReq := &bindIq{} |
640 if res != "" { |
634 if res != "" { |
641 bindReq.Resource = &res |
635 bindReq.Resource = &res |
642 } |
636 } |
643 msg := &Iq{Type: "set", Id: <- Id, Nested: []interface{}{bindReq}} |
637 msg := &Iq{Type: "set", Id: <-Id, Nested: []interface{}{bindReq}} |
644 f := func(st Stanza) bool { |
638 f := func(st Stanza) bool { |
645 if st.GetType() == "error" { |
639 if st.GetType() == "error" { |
646 if Log != nil { |
640 if Log != nil { |
647 Log.Err("Resource binding failed") |
641 Log.Err("Resource binding failed") |
648 } |
642 } |
649 return false |
643 return false |
650 } |
644 } |
651 var bindRepl *bindIq |
645 var bindRepl *bindIq |
652 for _, ele := range(st.GetNested()) { |
646 for _, ele := range st.GetNested() { |
653 if b, ok := ele.(*bindIq) ; ok { |
647 if b, ok := ele.(*bindIq); ok { |
654 bindRepl = b |
648 bindRepl = b |
655 break |
649 break |
656 } |
650 } |
657 } |
651 } |
658 if bindRepl == nil { |
652 if bindRepl == nil { |