Show More
@@ -556,6 +556,9 b" coreconfigitem('experimental', 'revlogv2" | |||||
556 | coreconfigitem('experimental', 'single-head-per-branch', |
|
556 | coreconfigitem('experimental', 'single-head-per-branch', | |
557 | default=False, |
|
557 | default=False, | |
558 | ) |
|
558 | ) | |
|
559 | coreconfigitem('experimental', 'sshserver.support-v2', | |||
|
560 | default=False, | |||
|
561 | ) | |||
559 | coreconfigitem('experimental', 'spacemovesdown', |
|
562 | coreconfigitem('experimental', 'spacemovesdown', | |
560 | default=False, |
|
563 | default=False, | |
561 | ) |
|
564 | ) |
@@ -409,9 +409,65 b' class sshv1protocolhandler(baseprotocolh' | |||||
409 | client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0] |
|
409 | client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0] | |
410 | return 'remote:ssh:' + client |
|
410 | return 'remote:ssh:' + client | |
411 |
|
411 | |||
|
412 | class sshv2protocolhandler(sshv1protocolhandler): | |||
|
413 | """Protocol handler for version 2 of the SSH protocol.""" | |||
|
414 | ||||
412 | def _runsshserver(ui, repo, fin, fout): |
|
415 | def _runsshserver(ui, repo, fin, fout): | |
|
416 | # This function operates like a state machine of sorts. The following | |||
|
417 | # states are defined: | |||
|
418 | # | |||
|
419 | # protov1-serving | |||
|
420 | # Server is in protocol version 1 serving mode. Commands arrive on | |||
|
421 | # new lines. These commands are processed in this state, one command | |||
|
422 | # after the other. | |||
|
423 | # | |||
|
424 | # protov2-serving | |||
|
425 | # Server is in protocol version 2 serving mode. | |||
|
426 | # | |||
|
427 | # upgrade-initial | |||
|
428 | # The server is going to process an upgrade request. | |||
|
429 | # | |||
|
430 | # upgrade-v2-filter-legacy-handshake | |||
|
431 | # The protocol is being upgraded to version 2. The server is expecting | |||
|
432 | # the legacy handshake from version 1. | |||
|
433 | # | |||
|
434 | # upgrade-v2-finish | |||
|
435 | # The upgrade to version 2 of the protocol is imminent. | |||
|
436 | # | |||
|
437 | # shutdown | |||
|
438 | # The server is shutting down, possibly in reaction to a client event. | |||
|
439 | # | |||
|
440 | # And here are their transitions: | |||
|
441 | # | |||
|
442 | # protov1-serving -> shutdown | |||
|
443 | # When server receives an empty request or encounters another | |||
|
444 | # error. | |||
|
445 | # | |||
|
446 | # protov1-serving -> upgrade-initial | |||
|
447 | # An upgrade request line was seen. | |||
|
448 | # | |||
|
449 | # upgrade-initial -> upgrade-v2-filter-legacy-handshake | |||
|
450 | # Upgrade to version 2 in progress. Server is expecting to | |||
|
451 | # process a legacy handshake. | |||
|
452 | # | |||
|
453 | # upgrade-v2-filter-legacy-handshake -> shutdown | |||
|
454 | # Client did not fulfill upgrade handshake requirements. | |||
|
455 | # | |||
|
456 | # upgrade-v2-filter-legacy-handshake -> upgrade-v2-finish | |||
|
457 | # Client fulfilled version 2 upgrade requirements. Finishing that | |||
|
458 | # upgrade. | |||
|
459 | # | |||
|
460 | # upgrade-v2-finish -> protov2-serving | |||
|
461 | # Protocol upgrade to version 2 complete. Server can now speak protocol | |||
|
462 | # version 2. | |||
|
463 | # | |||
|
464 | # protov2-serving -> protov1-serving | |||
|
465 | # Ths happens by default since protocol version 2 is the same as | |||
|
466 | # version 1 except for the handshake. | |||
|
467 | ||||
413 | state = 'protov1-serving' |
|
468 | state = 'protov1-serving' | |
414 | proto = sshv1protocolhandler(ui, fin, fout) |
|
469 | proto = sshv1protocolhandler(ui, fin, fout) | |
|
470 | protoswitched = False | |||
415 |
|
471 | |||
416 | while True: |
|
472 | while True: | |
417 | if state == 'protov1-serving': |
|
473 | if state == 'protov1-serving': | |
@@ -423,6 +479,19 b' def _runsshserver(ui, repo, fin, fout):' | |||||
423 | state = 'shutdown' |
|
479 | state = 'shutdown' | |
424 | continue |
|
480 | continue | |
425 |
|
481 | |||
|
482 | # It looks like a protocol upgrade request. Transition state to | |||
|
483 | # handle it. | |||
|
484 | if request.startswith(b'upgrade '): | |||
|
485 | if protoswitched: | |||
|
486 | _sshv1respondooberror(fout, ui.ferr, | |||
|
487 | b'cannot upgrade protocols multiple ' | |||
|
488 | b'times') | |||
|
489 | state = 'shutdown' | |||
|
490 | continue | |||
|
491 | ||||
|
492 | state = 'upgrade-initial' | |||
|
493 | continue | |||
|
494 | ||||
426 | available = wireproto.commands.commandavailable(request, proto) |
|
495 | available = wireproto.commands.commandavailable(request, proto) | |
427 |
|
496 | |||
428 | # This command isn't available. Send an empty response and go |
|
497 | # This command isn't available. Send an empty response and go | |
@@ -452,6 +521,103 b' def _runsshserver(ui, repo, fin, fout):' | |||||
452 | raise error.ProgrammingError('unhandled response type from ' |
|
521 | raise error.ProgrammingError('unhandled response type from ' | |
453 | 'wire protocol command: %s' % rsp) |
|
522 | 'wire protocol command: %s' % rsp) | |
454 |
|
523 | |||
|
524 | # For now, protocol version 2 serving just goes back to version 1. | |||
|
525 | elif state == 'protov2-serving': | |||
|
526 | state = 'protov1-serving' | |||
|
527 | continue | |||
|
528 | ||||
|
529 | elif state == 'upgrade-initial': | |||
|
530 | # We should never transition into this state if we've switched | |||
|
531 | # protocols. | |||
|
532 | assert not protoswitched | |||
|
533 | assert proto.name == SSHV1 | |||
|
534 | ||||
|
535 | # Expected: upgrade <token> <capabilities> | |||
|
536 | # If we get something else, the request is malformed. It could be | |||
|
537 | # from a future client that has altered the upgrade line content. | |||
|
538 | # We treat this as an unknown command. | |||
|
539 | try: | |||
|
540 | token, caps = request.split(b' ')[1:] | |||
|
541 | except ValueError: | |||
|
542 | _sshv1respondbytes(fout, b'') | |||
|
543 | state = 'protov1-serving' | |||
|
544 | continue | |||
|
545 | ||||
|
546 | # Send empty response if we don't support upgrading protocols. | |||
|
547 | if not ui.configbool('experimental', 'sshserver.support-v2'): | |||
|
548 | _sshv1respondbytes(fout, b'') | |||
|
549 | state = 'protov1-serving' | |||
|
550 | continue | |||
|
551 | ||||
|
552 | try: | |||
|
553 | caps = urlreq.parseqs(caps) | |||
|
554 | except ValueError: | |||
|
555 | _sshv1respondbytes(fout, b'') | |||
|
556 | state = 'protov1-serving' | |||
|
557 | continue | |||
|
558 | ||||
|
559 | # We don't see an upgrade request to protocol version 2. Ignore | |||
|
560 | # the upgrade request. | |||
|
561 | wantedprotos = caps.get(b'proto', [b''])[0] | |||
|
562 | if SSHV2 not in wantedprotos: | |||
|
563 | _sshv1respondbytes(fout, b'') | |||
|
564 | state = 'protov1-serving' | |||
|
565 | continue | |||
|
566 | ||||
|
567 | # It looks like we can honor this upgrade request to protocol 2. | |||
|
568 | # Filter the rest of the handshake protocol request lines. | |||
|
569 | state = 'upgrade-v2-filter-legacy-handshake' | |||
|
570 | continue | |||
|
571 | ||||
|
572 | elif state == 'upgrade-v2-filter-legacy-handshake': | |||
|
573 | # Client should have sent legacy handshake after an ``upgrade`` | |||
|
574 | # request. Expected lines: | |||
|
575 | # | |||
|
576 | # hello | |||
|
577 | # between | |||
|
578 | # pairs 81 | |||
|
579 | # 0000...-0000... | |||
|
580 | ||||
|
581 | ok = True | |||
|
582 | for line in (b'hello', b'between', b'pairs 81'): | |||
|
583 | request = fin.readline()[:-1] | |||
|
584 | ||||
|
585 | if request != line: | |||
|
586 | _sshv1respondooberror(fout, ui.ferr, | |||
|
587 | b'malformed handshake protocol: ' | |||
|
588 | b'missing %s' % line) | |||
|
589 | ok = False | |||
|
590 | state = 'shutdown' | |||
|
591 | break | |||
|
592 | ||||
|
593 | if not ok: | |||
|
594 | continue | |||
|
595 | ||||
|
596 | request = fin.read(81) | |||
|
597 | if request != b'%s-%s' % (b'0' * 40, b'0' * 40): | |||
|
598 | _sshv1respondooberror(fout, ui.ferr, | |||
|
599 | b'malformed handshake protocol: ' | |||
|
600 | b'missing between argument value') | |||
|
601 | state = 'shutdown' | |||
|
602 | continue | |||
|
603 | ||||
|
604 | state = 'upgrade-v2-finish' | |||
|
605 | continue | |||
|
606 | ||||
|
607 | elif state == 'upgrade-v2-finish': | |||
|
608 | # Send the upgrade response. | |||
|
609 | fout.write(b'upgraded %s %s\n' % (token, SSHV2)) | |||
|
610 | servercaps = wireproto.capabilities(repo, proto) | |||
|
611 | rsp = b'capabilities: %s' % servercaps.data | |||
|
612 | fout.write(b'%d\n%s\n' % (len(rsp), rsp)) | |||
|
613 | fout.flush() | |||
|
614 | ||||
|
615 | proto = sshv2protocolhandler(ui, fin, fout) | |||
|
616 | protoswitched = True | |||
|
617 | ||||
|
618 | state = 'protov2-serving' | |||
|
619 | continue | |||
|
620 | ||||
455 | elif state == 'shutdown': |
|
621 | elif state == 'shutdown': | |
456 | break |
|
622 | break | |
457 |
|
623 |
@@ -55,37 +55,6 b' class prehelloserver(wireprotoserver.ssh' | |||||
55 |
|
55 | |||
56 | super(prehelloserver, self).serve_forever() |
|
56 | super(prehelloserver, self).serve_forever() | |
57 |
|
57 | |||
58 | class upgradev2server(wireprotoserver.sshserver): |
|
|||
59 | """Tests behavior for clients that issue upgrade to version 2.""" |
|
|||
60 | def serve_forever(self): |
|
|||
61 | name = wireprotoserver.SSHV2 |
|
|||
62 | l = self._fin.readline() |
|
|||
63 | assert l.startswith(b'upgrade ') |
|
|||
64 | token, caps = l[:-1].split(b' ')[1:] |
|
|||
65 | assert caps == b'proto=%s' % name |
|
|||
66 |
|
||||
67 | # Filter hello and between requests. |
|
|||
68 | l = self._fin.readline() |
|
|||
69 | assert l == b'hello\n' |
|
|||
70 | l = self._fin.readline() |
|
|||
71 | assert l == b'between\n' |
|
|||
72 | l = self._fin.readline() |
|
|||
73 | assert l == b'pairs 81\n' |
|
|||
74 | self._fin.read(81) |
|
|||
75 |
|
||||
76 | # Send the upgrade response. |
|
|||
77 | proto = wireprotoserver.sshv1protocolhandler(self._ui, self._fin, |
|
|||
78 | self._fout) |
|
|||
79 | self._fout.write(b'upgraded %s %s\n' % (token, name)) |
|
|||
80 | servercaps = wireproto.capabilities(self._repo, proto) |
|
|||
81 | rsp = b'capabilities: %s' % servercaps.data |
|
|||
82 | self._fout.write(b'%d\n' % len(rsp)) |
|
|||
83 | self._fout.write(rsp) |
|
|||
84 | self._fout.write(b'\n') |
|
|||
85 | self._fout.flush() |
|
|||
86 |
|
||||
87 | super(upgradev2server, self).serve_forever() |
|
|||
88 |
|
||||
89 | def performhandshake(orig, ui, stdin, stdout, stderr): |
|
58 | def performhandshake(orig, ui, stdin, stdout, stderr): | |
90 | """Wrapped version of sshpeer._performhandshake to send extra commands.""" |
|
59 | """Wrapped version of sshpeer._performhandshake to send extra commands.""" | |
91 | mode = ui.config(b'sshpeer', b'handshake-mode') |
|
60 | mode = ui.config(b'sshpeer', b'handshake-mode') | |
@@ -118,8 +87,6 b' def extsetup(ui):' | |||||
118 | wireprotoserver.sshserver = bannerserver |
|
87 | wireprotoserver.sshserver = bannerserver | |
119 | elif servermode == b'no-hello': |
|
88 | elif servermode == b'no-hello': | |
120 | wireprotoserver.sshserver = prehelloserver |
|
89 | wireprotoserver.sshserver = prehelloserver | |
121 | elif servermode == b'upgradev2': |
|
|||
122 | wireprotoserver.sshserver = upgradev2server |
|
|||
123 | elif servermode: |
|
90 | elif servermode: | |
124 | raise error.ProgrammingError(b'unknown server mode: %s' % servermode) |
|
91 | raise error.ProgrammingError(b'unknown server mode: %s' % servermode) | |
125 |
|
92 |
@@ -453,9 +453,17 b' Send an upgrade request to a server that' | |||||
453 | local: no |
|
453 | local: no | |
454 | pushable: yes |
|
454 | pushable: yes | |
455 |
|
455 | |||
|
456 | Enable version 2 support on server. We need to do this in hgrc because we can't | |||
|
457 | use --config with `hg serve --stdio`. | |||
|
458 | ||||
|
459 | $ cat >> server/.hg/hgrc << EOF | |||
|
460 | > [experimental] | |||
|
461 | > sshserver.support-v2 = true | |||
|
462 | > EOF | |||
|
463 | ||||
456 | Send an upgrade request to a server that supports upgrade |
|
464 | Send an upgrade request to a server that supports upgrade | |
457 |
|
465 | |||
458 |
$ |
|
466 | $ hg -R server serve --stdio << EOF | |
459 | > upgrade this-is-some-token proto=exp-ssh-v2-0001 |
|
467 | > upgrade this-is-some-token proto=exp-ssh-v2-0001 | |
460 | > hello |
|
468 | > hello | |
461 | > between |
|
469 | > between | |
@@ -466,7 +474,7 b' Send an upgrade request to a server that' | |||||
466 | 383 |
|
474 | 383 | |
467 | capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN |
|
475 | capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN | |
468 |
|
476 | |||
469 |
$ |
|
477 | $ hg --config experimental.sshpeer.advertise-v2=true --debug debugpeer ssh://user@dummy/server | |
470 | running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) (no-windows !) |
|
478 | running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) (no-windows !) | |
471 | running * "*\tests/dummyssh" "user@dummy" "hg -R server serve --stdio" (glob) (windows !) |
|
479 | running * "*\tests/dummyssh" "user@dummy" "hg -R server serve --stdio" (glob) (windows !) | |
472 | sending upgrade request: * proto=exp-ssh-v2-0001 (glob) |
|
480 | sending upgrade request: * proto=exp-ssh-v2-0001 (glob) | |
@@ -482,7 +490,7 b' Send an upgrade request to a server that' | |||||
482 |
|
490 | |||
483 | Verify the peer has capabilities |
|
491 | Verify the peer has capabilities | |
484 |
|
492 | |||
485 |
$ |
|
493 | $ hg --config experimental.sshpeer.advertise-v2=true --debug debugcapabilities ssh://user@dummy/server | |
486 | running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) (no-windows !) |
|
494 | running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) (no-windows !) | |
487 | running * "*\tests/dummyssh" "user@dummy" "hg -R server serve --stdio" (glob) (windows !) |
|
495 | running * "*\tests/dummyssh" "user@dummy" "hg -R server serve --stdio" (glob) (windows !) | |
488 | sending upgrade request: * proto=exp-ssh-v2-0001 (glob) |
|
496 | sending upgrade request: * proto=exp-ssh-v2-0001 (glob) | |
@@ -527,3 +535,96 b' Verify the peer has capabilities' | |||||
527 | remote-changegroup |
|
535 | remote-changegroup | |
528 | http |
|
536 | http | |
529 | https |
|
537 | https | |
|
538 | ||||
|
539 | Command after upgrade to version 2 is processed | |||
|
540 | ||||
|
541 | $ hg -R server serve --stdio << EOF | |||
|
542 | > upgrade this-is-some-token proto=exp-ssh-v2-0001 | |||
|
543 | > hello | |||
|
544 | > between | |||
|
545 | > pairs 81 | |||
|
546 | > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000hello | |||
|
547 | > EOF | |||
|
548 | upgraded this-is-some-token exp-ssh-v2-0001 | |||
|
549 | 383 | |||
|
550 | capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN | |||
|
551 | 384 | |||
|
552 | capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN | |||
|
553 | ||||
|
554 | Multiple upgrades is not allowed | |||
|
555 | ||||
|
556 | $ hg -R server serve --stdio << EOF | |||
|
557 | > upgrade this-is-some-token proto=exp-ssh-v2-0001 | |||
|
558 | > hello | |||
|
559 | > between | |||
|
560 | > pairs 81 | |||
|
561 | > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000upgrade another-token proto=irrelevant | |||
|
562 | > hello | |||
|
563 | > EOF | |||
|
564 | upgraded this-is-some-token exp-ssh-v2-0001 | |||
|
565 | 383 | |||
|
566 | capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN | |||
|
567 | cannot upgrade protocols multiple times | |||
|
568 | - | |||
|
569 | ||||
|
570 | ||||
|
571 | Malformed upgrade request line (not exactly 3 space delimited tokens) | |||
|
572 | ||||
|
573 | $ hg -R server serve --stdio << EOF | |||
|
574 | > upgrade | |||
|
575 | > EOF | |||
|
576 | 0 | |||
|
577 | ||||
|
578 | $ hg -R server serve --stdio << EOF | |||
|
579 | > upgrade token | |||
|
580 | > EOF | |||
|
581 | 0 | |||
|
582 | ||||
|
583 | $ hg -R server serve --stdio << EOF | |||
|
584 | > upgrade token foo=bar extra-token | |||
|
585 | > EOF | |||
|
586 | 0 | |||
|
587 | ||||
|
588 | Upgrade request to unsupported protocol is ignored | |||
|
589 | ||||
|
590 | $ hg -R server serve --stdio << EOF | |||
|
591 | > upgrade this-is-some-token proto=unknown1,unknown2 | |||
|
592 | > hello | |||
|
593 | > between | |||
|
594 | > pairs 81 | |||
|
595 | > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 | |||
|
596 | > EOF | |||
|
597 | 0 | |||
|
598 | 384 | |||
|
599 | capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN | |||
|
600 | 1 | |||
|
601 | ||||
|
602 | ||||
|
603 | Upgrade request must be followed by hello + between | |||
|
604 | ||||
|
605 | $ hg -R server serve --stdio << EOF | |||
|
606 | > upgrade token proto=exp-ssh-v2-0001 | |||
|
607 | > invalid | |||
|
608 | > EOF | |||
|
609 | malformed handshake protocol: missing hello | |||
|
610 | - | |||
|
611 | ||||
|
612 | ||||
|
613 | $ hg -R server serve --stdio << EOF | |||
|
614 | > upgrade token proto=exp-ssh-v2-0001 | |||
|
615 | > hello | |||
|
616 | > invalid | |||
|
617 | > EOF | |||
|
618 | malformed handshake protocol: missing between | |||
|
619 | - | |||
|
620 | ||||
|
621 | ||||
|
622 | $ hg -R server serve --stdio << EOF | |||
|
623 | > upgrade token proto=exp-ssh-v2-0001 | |||
|
624 | > hello | |||
|
625 | > between | |||
|
626 | > invalid | |||
|
627 | > EOF | |||
|
628 | malformed handshake protocol: missing pairs 81 | |||
|
629 | - | |||
|
630 |
General Comments 0
You need to be logged in to leave comments.
Login now