Browse Source

Plan 9 from Bell Labs 2012-04-19

David du Colombier 12 years ago
parent
commit
cc58638470
68 changed files with 15664 additions and 121 deletions
  1. 33 19
      lib/ndb/common
  2. 2 2
      rc/bin/cpurc
  3. 150 30
      sys/lib/man/lookman/index
  4. 9 4
      sys/man/1/INDEX
  5. 22 6
      sys/man/1/INDEX.html
  6. 61 43
      sys/man/1/ssh1
  7. 322 0
      sys/man/1/ssh2
  8. 5 0
      sys/man/2/INDEX
  9. 2 2
      sys/man/2/INDEX.html
  10. 1 1
      sys/man/3/INDEX.html
  11. 1 0
      sys/man/4/INDEX
  12. 4 0
      sys/man/4/INDEX.html
  13. 535 0
      sys/man/4/ssh
  14. 6 0
      sys/man/8/INDEX
  15. 10 2
      sys/man/8/INDEX.html
  16. 0 0
      sys/man/searchindex
  17. 0 0
      sys/src/cmd/ssh1/agent.c
  18. 0 0
      sys/src/cmd/ssh1/authpasswd.c
  19. 0 0
      sys/src/cmd/ssh1/authrsa.c
  20. 0 0
      sys/src/cmd/ssh1/authsrvpasswd.c
  21. 0 0
      sys/src/cmd/ssh1/authsrvtis.c
  22. 0 0
      sys/src/cmd/ssh1/authtis.c
  23. 0 0
      sys/src/cmd/ssh1/cipher3des.c
  24. 0 0
      sys/src/cmd/ssh1/cipherblowfish.c
  25. 0 0
      sys/src/cmd/ssh1/cipherdes.c
  26. 0 0
      sys/src/cmd/ssh1/ciphernone.c
  27. 0 0
      sys/src/cmd/ssh1/cipherrc4.c
  28. 0 0
      sys/src/cmd/ssh1/ciphertwiddle.c
  29. 0 0
      sys/src/cmd/ssh1/cmsg.c
  30. 2 2
      sys/src/cmd/ssh1/mkfile
  31. 0 0
      sys/src/cmd/ssh1/msg.c
  32. 0 0
      sys/src/cmd/ssh1/pubkey.c
  33. 0 0
      sys/src/cmd/ssh1/scp.c
  34. 0 0
      sys/src/cmd/ssh1/smsg.c
  35. 0 0
      sys/src/cmd/ssh1/ssh.h
  36. 1 0
      sys/src/cmd/ssh1/ssh1.c
  37. 0 0
      sys/src/cmd/ssh1/sshnet.c
  38. 0 0
      sys/src/cmd/ssh1/sshserve.c
  39. 0 0
      sys/src/cmd/ssh1/util.c
  40. 27 0
      sys/src/cmd/ssh2/_changes
  41. 49 0
      sys/src/cmd/ssh2/cipher3des.c
  42. 91 0
      sys/src/cmd/ssh2/cipheraes.c
  43. 66 0
      sys/src/cmd/ssh2/cipherblowfish.c
  44. 46 0
      sys/src/cmd/ssh2/cipherrc4.c
  45. 30 0
      sys/src/cmd/ssh2/common.c
  46. 930 0
      sys/src/cmd/ssh2/dh.c
  47. 559 0
      sys/src/cmd/ssh2/dial.c
  48. 553 0
      sys/src/cmd/ssh2/dial.thread.c
  49. 3509 0
      sys/src/cmd/ssh2/dup.out
  50. 2638 0
      sys/src/cmd/ssh2/dup.pro
  51. 36 0
      sys/src/cmd/ssh2/dup.sum
  52. 235 0
      sys/src/cmd/ssh2/dup.xpr
  53. 17 0
      sys/src/cmd/ssh2/esmprint.c
  54. 39 0
      sys/src/cmd/ssh2/funclen
  55. 79 0
      sys/src/cmd/ssh2/long.funcs
  56. 5 0
      sys/src/cmd/ssh2/magic
  57. 290 0
      sys/src/cmd/ssh2/magic.out
  58. 75 0
      sys/src/cmd/ssh2/mkfile
  59. 3210 0
      sys/src/cmd/ssh2/netssh.c
  60. 351 0
      sys/src/cmd/ssh2/netssh.h
  61. 226 0
      sys/src/cmd/ssh2/pubkey.c
  62. 66 0
      sys/src/cmd/ssh2/rsa2ssh2.c
  63. 587 0
      sys/src/cmd/ssh2/ssh.c
  64. 29 0
      sys/src/cmd/ssh2/ssh2.h
  65. 497 0
      sys/src/cmd/ssh2/sshsession.c
  66. 236 0
      sys/src/cmd/ssh2/transport.c
  67. 1 10
      sys/src/libc/arm/getfcr.s
  68. 21 0
      sys/src/libc/arm/getfcr.vfp.S

+ 33 - 19
lib/ndb/common

@@ -5,19 +5,28 @@
 #
 # real dns root server ips
 #
-dom=A.ROOT-SERVERS.NET ip=198.41.0.4
-dom=B.ROOT-SERVERS.NET ip=128.9.0.107
-dom=C.ROOT-SERVERS.NET ip=192.33.4.12
-dom=D.ROOT-SERVERS.NET ip=128.8.10.90
-dom=E.ROOT-SERVERS.NET ip=192.203.230.10
-dom=F.ROOT-SERVERS.NET ip=192.5.5.241
-dom=G.ROOT-SERVERS.NET ip=192.112.36.4
-dom=H.ROOT-SERVERS.NET ip=128.63.2.53
-dom=I.ROOT-SERVERS.NET ip=192.36.148.17
-dom=J.ROOT-SERVERS.NET ip=198.41.0.10
-dom=K.ROOT-SERVERS.NET ip=193.0.14.129
-dom=L.ROOT-SERVERS.NET ip=199.7.83.42		# nov 2007
-dom=M.ROOT-SERVERS.NET ip=202.12.27.33
+dom=a.root-servers.net ip=198.41.0.4
+dom=b.root-servers.net ip=128.9.0.107
+dom=c.root-servers.net ip=192.33.4.12
+dom=d.root-servers.net ip=128.8.10.90
+dom=e.root-servers.net ip=192.203.230.10
+dom=f.root-servers.net ip=192.5.5.241
+dom=g.root-servers.net ip=192.112.36.4
+dom=h.root-servers.net ip=128.63.2.53
+dom=i.root-servers.net ip=192.36.148.17
+dom=j.root-servers.net ip=198.41.0.10
+dom=k.root-servers.net ip=193.0.14.129
+dom=l.root-servers.net ip=199.7.83.42		# nov 2007
+dom=m.root-servers.net ip=202.12.27.33
+
+# dom=a.root-servers.net ipv6=2001:503:ba3e::2:30
+# dom=f.root-servers.net ipv6=2001:500:2f::f
+# dom=h.root-servers.net ipv6=2001:500:1::803f:235
+# dom=i.root-servers.net ipv6=2001:7fe::53
+# dom=j.root-servers.net ipv6=2001:503:c27::2:30
+# dom=k.root-servers.net ipv6=2001:7fd::1
+# dom=l.root-servers.net ipv6=2001:500:3::42
+# dom=m.root-servers.net ipv6=2001:dc3::35
 
 dom=a.gtld-servers.net ip=192.5.6.30
 dom=b.gtld-servers.net ip=192.33.14.30
@@ -33,6 +42,9 @@ dom=k.gtld-servers.net ip=192.52.178.30
 dom=l.gtld-servers.net ip=192.41.162.30
 dom=m.gtld-servers.net ip=192.55.83.30
 
+# dom=a.gtld-servers.net ipv6=2001:503:a83e::2:30
+# dom=b.gtld-servers.net ipv6=2001:503:231d::2:30
+
 #
 # spam defense.  unfortunately, arin doesn't give negative
 # rcodes for these non-routable addresses.  we'll do it for them.
@@ -183,7 +195,7 @@ tcp=netstat port=15
 tcp=chargen port=19
 tcp=ftp-data port=20
 tcp=ftp port=21
-tcp=ssh port=22
+tcp=ssh port=22		# v2 nowadays
 tcp=telnet port=23
 tcp=smtp port=25
 tcp=time port=37
@@ -261,7 +273,7 @@ tcp=infsigner port=6671
 tcp=infcsigner port=6672
 tcp=inflogin port=6673
 tcp=bandt port=7330
-tcp=face port=32000
+tcp=ccnx port=9695
 tcp=dhashgate port=11978
 tcp=exportfs port=17007
 tcp=rexexec port=17009
@@ -277,14 +289,13 @@ tcp=glenglenda7 port=17026
 tcp=glenglenda8 port=17027
 tcp=glenglenda9 port=17028
 tcp=glenglenda10 port=17029
-tcp=nupasimap port=17030
-tcp=nupasimaps port=17031
 tcp=flyboy port=17032
 tcp=venti port=17034
 tcp=wiki port=17035
 tcp=vica port=17036
 tcp=aan port=17037
-
+tcp=ssh1 port=17122		# v1
+tcp=face port=32000
 
 # testing
 tcp=9fsa port=18008
@@ -314,6 +325,7 @@ udp=sip port=5060
 udp=bandt2 port=7331		# viaducts
 udp=oradius port=1812
 udp=radius port=1812
+udp=ccnx port=9695
 udp=dhash port=11977
 udp=ulctl port=12666
 udp=uldata port=12667
@@ -321,6 +333,8 @@ udp=dldata port=12668
 
 gre=ppp port=34827
 
+ssh=ssh port=22
+
 #
 # authdom declarations need to be visible on the inside network,
 # even for outside machines.  putting them here ensures
@@ -332,7 +346,7 @@ auth=www.9netics.com authdom=9netics.com
 auth=newcpu.9netics.net authdom=9netics.net
 auth=mordor.tip9ug.jp authdom=tip9ug.jp
 
-# for geoff's ipv6 testing
+# for geoff's external ipv6 testing
 auth=9grid.hamnavoe.com
 	authdom=hamnavoe.com
 	authdom=9grid.hamnavoe.com

+ 2 - 2
rc/bin/cpurc

@@ -41,8 +41,6 @@ if(! test -e /srv/dns)
 # auth/keyfs -wp -m /mnt/keys /adm/keys >/dev/null >[2=1]
 # auth/cron >>/sys/log/cron >[2=1] &
 
-# sshtun
-
 # keep other bootes processes from creating capabilities
 rm -f '#¤/caphash'
 
@@ -55,6 +53,8 @@ rm -f '#¤/caphash'
 #	mv /rc/bin/service/tcp567 /rc/bin/service/_tcp567
 # }
 
+# netssh
+
 # start listeners if it hasn't already been done (dicey check)
 if(! netstat -n | grep -s 'tcp.*Listen.* (7|9|21|22|23|25|110|113|565|993|17007|17009|17010) .*')
 	aux/listen -q tcp

File diff suppressed because it is too large
+ 150 - 30
sys/lib/man/lookman/index


+ 9 - 4
sys/man/1/INDEX

@@ -7,6 +7,7 @@ intro 0intro
 6a 2a
 7a 2a
 8a 2a
+9a 2a
 ka 2a
 qa 2a
 va 2a
@@ -17,6 +18,7 @@ va 2a
 6c 2c
 7c 2c
 8c 2c
+9c 2c
 kc 2c
 qc 2c
 vc 2c
@@ -27,6 +29,7 @@ vc 2c
 6l 2l
 7l 2l
 8l 2l
+9l 2l
 kl 2l
 ql 2l
 vl 2l
@@ -54,6 +57,7 @@ light bitsyload
 params bitsyload
 pencal bitsyload
 prompter bitsyload
+bsplit bsplit
 bundle bundle
 cal cal
 calendar calendar
@@ -275,10 +279,8 @@ sprog spell
 spin spin
 split split
 src src
-scp ssh
-ssh ssh
-sshnet ssh
-sshserve ssh
+ssh1 ssh1
+ssh2 ssh2
 start stop
 stop stop
 strings strings
@@ -290,6 +292,8 @@ syscall syscall
 tail tail
 dircp tar
 tar tar
+tarcat tarsplit
+tarsplit tarsplit
 tbl tbl
 tcs tcs
 tee tee
@@ -298,6 +302,7 @@ tel tel
 test test
 thesaurus thesaurus
 time time
+tinyurl tinyurl
 touch touch
 tr tr
 trace trace

+ 22 - 6
sys/man/1/INDEX.html

@@ -12,15 +12,15 @@
 </DT>
 <DT><A HREF="/magic/man2html/1/2a">2a</A>
 -  assemblers
-<DD><TT> 0a, 1a, 2a, 5a, 6a, 7a, 8a, ka, qa, va</TT>
+<DD><TT> 0a, 1a, 2a, 5a, 6a, 7a, 8a, 9a, ka, qa, va</TT>
 </DT>
 <DT><A HREF="/magic/man2html/1/2c">2c</A>
 -  C compilers
-<DD><TT> 0c, 1c, 2c, 5c, 6c, 7c, 8c, kc, qc, vc</TT>
+<DD><TT> 0c, 1c, 2c, 5c, 6c, 7c, 8c, 9c, kc, qc, vc</TT>
 </DT>
 <DT><A HREF="/magic/man2html/1/2l">2l</A>
 -  loaders
-<DD><TT> 0l, 1l, 2l, 5l, 6l, 7l, 8l, kl, ql, vl</TT>
+<DD><TT> 0l, 1l, 2l, 5l, 6l, 7l, 8l, 9l, kl, ql, vl</TT>
 </DT>
 <DT><A HREF="/magic/man2html/1/abaco">abaco</A>
 -  browse the World-Wide Web
@@ -62,6 +62,10 @@
 -  bitsy-specific utilities
 <DD><TT> bitsyload, light, pencal, keyboard, params, prompter</TT>
 </DT>
+<DT><A HREF="/magic/man2html/1/bsplit">bsplit</A>
+-  split binary data into fixed-size chunks
+<DD><TT> bsplit</TT>
+</DT>
 <DT><A HREF="/magic/man2html/1/bundle">bundle</A>
 -  collect files for distribution
 <DD><TT> bundle</TT>
@@ -534,9 +538,13 @@
 -  find source code for executable
 <DD><TT> src</TT>
 </DT>
-<DT><A HREF="/magic/man2html/1/ssh">ssh</A>
--  secure login and file copy from/to Unix or Plan 9
-<DD><TT> ssh, sshnet, scp, sshserve</TT>
+<DT><A HREF="/magic/man2html/1/ssh1">ssh1</A>
+-  encrypted login and file copy with foreign systems via SSHv1
+<DD><TT> ssh1, sshnet, scp, sshserve</TT>
+</DT>
+<DT><A HREF="/magic/man2html/1/ssh2">ssh2</A>
+-  encrypted login and copy with foreign systems via SSHv2
+<DD><TT> ssh, sshsession, rsa2ssh2</TT>
 </DT>
 <DT><A HREF="/magic/man2html/1/stop">stop</A>
 -  print commands to stop and start processes
@@ -566,6 +574,10 @@
 -  archiver
 <DD><TT> tar, dircp</TT>
 </DT>
+<DT><A HREF="/magic/man2html/1/tarsplit">tarsplit</A>
+-  split a tar archive into archives under some size, or combine archives
+<DD><TT> tarsplit, tarcat</TT>
+</DT>
 <DT><A HREF="/magic/man2html/1/tbl">tbl</A>
 -  format tables for nroff or troff
 <DD><TT> tbl</TT>
@@ -594,6 +606,10 @@
 -  time a command
 <DD><TT> time</TT>
 </DT>
+<DT><A HREF="/magic/man2html/1/tinyurl">tinyurl</A>
+-  shrink a URL
+<DD><TT> tinyurl</TT>
+</DT>
 <DT><A HREF="/magic/man2html/1/touch">touch</A>
 -  set modification date of a file
 <DD><TT> touch</TT>

+ 61 - 43
sys/man/1/ssh → sys/man/1/ssh1

@@ -1,8 +1,8 @@
-.TH SSH 1
+.TH SSH1 1
 .SH NAME
-ssh, sshnet, scp, sshserve \- secure login and file copy from/to Unix or Plan 9
+ssh1, sshnet, scp, sshserve \- encrypted login and file copy with foreign systems via SSHv1
 .SH SYNOPSIS
-.B ssh
+.B ssh1
 [
 .B -CfiImPpRrw
 ]
@@ -45,28 +45,42 @@ ssh, sshnet, scp, sshserve \- secure login and file copy from/to Unix or Plan 9
 .RI [ user\fB@ ] host
 .PP
 .B scp
-[host:]file [host:]file
+.RI [ host :] file
+.RI [ host2 :] file2
 .br
 .B scp
-[host:]file ... [host:]dir
+.RI [ host :] file
+\&...
+.RI [ host2 :] dir
 .PP
 .B aux/sshserve
 [
-.B -p
+.B -A
+.I authlist
+]
+[
+.B -c
+.I cipherlist
+]
+[
+.B -i
+.I id-string
 ]
 .I address
 .SH DESCRIPTION
-.I Ssh
-allows authenticated login over an encrypted channel to hosts that
-support the ssh protocol (see the RFCs listed below for encryption and
+.I Ssh1
+performs authenticated login over an encrypted channel to hosts that
+support the SSH v1 protocol (see the RFCs listed below for encryption and
 authentication details).
-.LP
-.I Ssh
+.I Ssh1
 takes the host name of the machine to connect to as its mandatory argument.
 It may be specified as a domain name or an IP address.
-Normally, login is attempted using the user name from /dev/user.
+Normally, login is attempted using the user name from
+.BR /dev/user .
 .PP
 Command-line options are:
+.TF "C 
+.PD
 .TP
 .B -C
 force input to be read in cooked mode:
@@ -75,7 +89,7 @@ force input to be read in cooked mode:
 .B -f
 enable agent forwarding.
 With this flag,
-.I ssh
+.I ssh1
 uses SSH's agent forwarding protocol to allow
 programs running on the remote server to
 interact with
@@ -85,14 +99,14 @@ to perform RSA authentication.
 .B -i
 force interactive mode.
 In interactive mode, 
-.I ssh
+.I ssh1
 prompts for passwords and confirmations of
 new host keys when necessary.
 (In non-interactive mode, password requests
 are rejected and unrecognized host keys are 
 cause for disconnecting.)
 By default, 
-.I ssh 
+.I ssh 1
 runs in interactive mode only when its 
 input file descriptor is 
 .BR /dev/cons .
@@ -107,14 +121,12 @@ menu, described below.
 .TP
 .B -p
 force pseudoterminal request.
-The
-.I ssh
-protocol, grounded in Unix tradition,
+The SSH protocol, grounded in Unix tradition,
 differentiates between connections
 that request controlling pseudoterminals
 and those that do not.
 By default, 
-.I ssh
+.I ssh1
 requests a pseudoterminal only when no
 .I command
 is given.
@@ -199,33 +211,33 @@ a login session is started on the remote
 host.
 Otherwise, the command is executed with its arguments.
 .LP
-.I Ssh
-establishes a connection with an ssh daemon on the remote host.
+.I Ssh1
+establishes a connection with an SSH daemon on the remote host.
 The daemon sends to 
-.I ssh
+.I ssh1
 its RSA public host key and session key.
 Using these,
-.I ssh
+.I ssh1
 sends a session key which, presumably, only the
 daemon can decipher.  After this, both sides start encrypting their
 data with this session key.
 .LP
 When the daemon's host key has been received,
-.I ssh
+.I ssh1
 looks it up in 
 .B $home/lib/keyring
 and in 
 .BR /sys/lib/ssh/keyring .
 If
 the key is found there, and it matches the received key,
-.I ssh
+.I ssh1
 is satisfied.  If not,
-.I ssh
+.I ssh1
 reports this and offers to add the key to
 .BR $home/lib/keyring .
 .LP
 Over the encrypted channel,
-.I ssh
+.I ssh1
 attempts to convince the daemon to accept the call
 using the listed authentication protocols
 (see the
@@ -235,8 +247,7 @@ option above).
 The preferred way to authenticate is a
 .IR netkey -style
 challenge/response or via a SecurID token.
-.I Ssh
-users on other systems than Plan 9 should enable \s-2TIS_A\s0uthentication.
+SSH users on other systems than Plan 9 should enable \s-2TIS_A\s0uthentication.
 .LP
 When the connection is authenticated, the given command line,
 (by default, a login shell) is executed on the remote host.
@@ -253,38 +264,39 @@ mounted at
 .BR /net ),
 optionally posting a 9P service
 descriptor for the new file system as
-.IB /srv/ service \fR.
+.BI /srv/ service \fR.
 The
 .B -A
 and
 .B -c
-arguments are as in
-.IR ssh .
+arguments are as for
+.IR ssh1 .
 .sp 1
 .I Scp
 uses
 .I ssh
-to copy files from one host to another.  A remote file is identified by
+to copy files from one host to another.
+A remote file is identified by
 a host name, a colon and a file name (no spaces).
 .I Scp
 can copy files from remote hosts and to remote hosts.
-.sp 1
+.SS "Server and Keys
 .I Sshserve
 is the server that services
-.I ssh
+SSH
 calls from remote hosts. 
 The 
 .B -A
 and
 .B -c
 options set valid authentication methods and ciphers
-as in
-.IR ssh ,
+as for
+.IR ssh1 ,
 except that there is no
 .B rsa
 authentication method.
 Unlike in
-.IR ssh ,
+.IR ssh1 ,
 the list is not ordered: the server presents a set and the client makes the choice.
 The default sets are
 .B tis
@@ -292,6 +304,11 @@ and
 .B blowfish
 .B rc4
 .BR 3des .
+The
+.B -i
+option prevents reading the client's ID-string line and assumes
+its ID string to be
+.IR id-string .
 By default, users start with the namespace defined in
 .BR /lib/namespace .
 Users in group
@@ -329,18 +346,19 @@ grep 'service=sshserve' /mnt/factotum/ctl | auth/rsa2ssh
 .SH FILES
 .TP
 .B /sys/lib/ssh/keyring
-System key ring file containing public keys for remote ssh clients and servers.
+System key ring file containing public keys for remote SSH clients and servers.
 .TP
 .B /usr/\fIuser\fP/lib/keyring
-Personal key ring file containing public keys for remote ssh clients and
+Personal key ring file containing public keys for remote SSH clients and
 servers.
 .SH SOURCE
-.B /sys/src/cmd/ssh
+.B /sys/src/cmd/ssh1
 .SH "SEE ALSO"
 .B /lib/rfc/rfc425[0-6]
 .br
+.IR con (1),
+.IR cpu (1),
+.IR ssh2 (1),
 .IR factotum (4),
 .IR authsrv (6),
 .IR rsa (8)
-.SH BUGS
-Only version 1 of the SSH protocol is implemented.

+ 322 - 0
sys/man/1/ssh2

@@ -0,0 +1,322 @@
+.TH SSH2 1
+.SH NAME
+ssh, sshsession, rsa2ssh2 \- encrypted login and copy with foreign systems via SSHv2
+.SH SYNOPSIS
+.B ssh
+[
+.B -adIiKkmrvx
+] [
+.B -l
+.I user
+] [
+.B -n
+.I dir
+] [
+.B -z
+.I attribute=value
+]
+system
+[
+.I cmd
+[
+.I args
+] ]
+.PP
+.B aux/sshsession
+[
+.B -t
+] [
+.B -n
+.I namespace
+] [
+.B -R
+.I dir
+] [
+.B -r
+.I dir
+] [
+.B -s
+.I command
+] [
+.B -S
+.I srvpt
+]
+.PP
+.B rsa2ssh2
+[
+.I file
+]
+.SH DESCRIPTION
+These programs collectively implement communication via SSH v2 over TCP.
+All of the encryption, authentication, and SSH protocol are handled by
+.IR ssh (4).
+.PP
+.I Ssh
+dials a remote
+.I system
+and runs a shell (or some other command) there.
+.L "ssh root@hannibal"
+will result in a command prompt on the machine
+.B hannibal
+logged in as
+.BR root .
+If
+.I ssh
+does not find an SSH server in
+.BR /net ,
+it runs
+.I netssh
+(see
+.IR ssh (4))
+to start one.
+.I Ssh
+dials the remote SSH server and exchanges encryption keys with
+the server using Diffie-Hellman key exchange.
+.PP
+A
+.B clone
+file and
+.B connect
+message protocol
+similar to that of
+.IR ip (3)
+creates a session in the established connection.
+In the course of session creation,
+.I ssh
+first attempts to authenticate the user with the server using
+public key authentication.
+If that fails, it prompts for a password, and attempts to
+authenticate with password authentication.
+It also passes across the value of the environment variable
+.B TERM
+as would be set if
+.I ssh
+is run inside of
+.IR vt (1).
+.LP
+Per
+.IR con (1),
+typing a control-\e will result in a
+.B >>>
+prompt.
+There are currently only four commands that can be issued at that prompt:
+.B c
+to continue the session,
+.B h
+to print a list of the available commands,
+.B r
+to toggle the suppression of carriage returns, and
+.B q
+to close the session.
+.LP
+Options are:
+.TF "-i -
+.TP
+.B -a -v -x
+No-ops included for compatibility with
+.I scp
+(see
+.IR ssh1 (1)).
+.TP
+.B -d
+Increase the amount of debugging output.
+.TP
+.B -i -I
+Sets
+.I ssh
+to interactive
+.RB ( -i )
+or non-interactive
+.RI ( -I )
+mode.
+This determines whether the user is prompted for a password
+if none is found in factotum.
+Without either of these options,
+.I ssh
+uses interactive mode if run in a term window.
+.TP
+.B -k
+Skip the attempt to authenticate using public key authentication.
+.TP
+.B -K
+Don't fall back to password authentication.
+If the public key authentication fails,
+.I ssh
+will exit.
+.TP
+.B -l
+Use
+.I user
+name on the remote system
+(deprecated).
+.TP
+.B -m
+Remove the special meaning of control-\e.
+This is needed by
+.I scp
+to prevent that character in files being copied from triggering the
+special command mode.
+.TP
+.B -n
+Specify the network directory of an alternate network to use.
+The default is
+.BR /net .
+.TP
+.B -r
+Strip carriage return characters coming from the remote system.
+This will normally be desired when running in a
+.IR rio (1)
+window or from within
+.IR win (1)
+in
+.IR acme (1).
+It is normally not used when running
+.I ssh
+from within
+.IR vt (1).
+.TP
+.B -z
+Used to specify which of several possible keys to use.
+.PD
+.
+.SS "Server-side Daemon"
+.I Sshsession
+implements the server side of an SSH connection
+and is suitable for running by
+.IR listen (8)
+or
+.IR listen1 ;
+it is not normally run directly by the user.
+Like
+.IR ssh ,
+it does all of its SSH communication through
+.IR ssh (4).
+.I Sshsession
+starts a shell or a requested command when a remote
+system authenticates and requests a new connection and session.
+.LP
+A system-wide SSH listener can be run by creating a file
+.BR /rc/bin/service.auth/ssh22
+that invokes
+.IR sshsession :
+.IP
+.EX
+#!/bin/rc
+# ssh22
+exec aux/sshsession $3 >>/sys/log/sshdebug >[2=1]
+.EE
+.LP
+.ne 3
+then ensuring that
+.B /rc/bin/cpurc
+contains
+.IP
+.EX
+netssh -s ssh
+aux/listen -t /rc/bin/service.auth -d /rc/bin/service ssh
+.EE
+.LP
+When invoked with no options,
+.I sshsession
+runs as an SSH server.
+Options are:
+.TF -S
+.TP
+.B -n
+Use a
+.I namespace
+other than the default
+.B /lib/namespace
+when starting the shell or running the requested command.
+.TP
+.B -r
+Run the SSH session in
+.IR dir .
+.TP
+.B -R
+Like
+.B -r
+but also prevent any arguments, in the command to be executed,
+from accessing files outside this directory;
+primarily used to limit what
+.I scp
+can access.
+.TP
+.B -s
+Execute
+.I command
+instead of
+.BR /bin/rc .
+.TP
+.B -S
+Create
+.BI /srv/ srvpt
+if an SSH server is not already mounted in
+.BR /net .
+.TP
+.B -t
+Trust
+.I sshsession
+and run it in the same namespace as the
+.I listen
+that started it.
+.PD
+.LP
+A private SSH listener can be run by starting
+.I netssh
+(see
+.IR ssh (4))
+then running
+.IR listen1 :
+.IP
+.EX
+if (! test -e /net/ssh) netssh -s ssh
+aux/listen1 -t 'ssh!*!2222' aux/sshsession
+.EE
+.
+.SS Keys
+.I Rsa2ssh2
+converts an RSA key to one suitable for use with SSH v2 on Unix systems.
+The following command will extract the public part of the key and add it to the
+.B authorized_keys
+file on a remote Unix system:
+.IP
+.EX
+grep 'proto=rsa' /mnt/factotum/ctl | rsa2ssh2 |
+	ssh2 user@unix 'cat >>.ssh/authorized_keys'
+.EE
+.SH FILES
+.TF /sys/lib/ssh/keyring
+.TP
+.B /sys/lib/ssh/keyring
+System-wide known host public keys.
+.TP
+.B $home/lib/keyring
+Per-user known host public keys.
+.TP
+.B /env/nosshkeyverify
+.SH SOURCE
+.B /sys/src/cmd/ssh2
+.SH "SEE ALSO"
+.IR con (1),
+.IR cpu (1),
+.IR secstore (1),
+.IR ssh1 (1),
+.IR vt (1),
+.IR factotum (4),
+.IR ssh (4),
+.IR listen (8),
+.br
+RFCs 4250, 4251, 4252, 4253, 4254, and 4419
+.SH BUGS
+.I Sshsession
+shouldn't have to run as the host owner and using
+.IR factotum (4)
+correctly would permit this.
+.PP
+The SSH v2 protocol is a classic second system:
+over-engineered,
+overly complicated,
+misdesigned
+and
+jammed full of pointless goodies.

+ 5 - 0
sys/man/2/INDEX

@@ -765,6 +765,7 @@ get1 mach
 get2 mach
 get4 mach
 get8 mach
+geta mach
 leswab mach
 leswal mach
 leswav mach
@@ -777,6 +778,7 @@ put1 mach
 put2 mach
 put4 mach
 put8 mach
+puta mach
 setmap mach
 unusemap mach
 calloc malloc
@@ -1234,11 +1236,13 @@ strspn strcat
 strstr strcat
 strtok strcat
 s_alloc string
+s_allocinstack string
 s_append string
 s_array string
 s_copy string
 s_error string
 s_free string
+s_freeinstack string
 s_getline string
 s_grow string
 s_incref string
@@ -1248,6 +1252,7 @@ s_new string
 s_newalloc string
 s_parse string
 s_putc string
+s_rdinstack string
 s_read string
 s_read_line string
 s_reset string

+ 2 - 2
sys/man/2/INDEX.html

@@ -328,7 +328,7 @@
 </DT>
 <DT><A HREF="/magic/man2html/2/mach">mach</A>
 -  machine-independent access to executable files
-<DD><TT> crackhdr, machbytype, machbyname, newmap, setmap, findseg, unusemap, loadmap, attachproc, get1, get2, get4, get8, put1, put2, put4, put8, beswab, beswal, beswav, leswab, leswal, leswav</TT>
+<DD><TT> crackhdr, machbytype, machbyname, newmap, setmap, findseg, unusemap, loadmap, attachproc, get1, get2, get4, get8, geta, put1, put2, put4, put8, puta beswab, beswal, beswav, leswab, leswal, leswav</TT>
 </DT>
 <DT><A HREF="/magic/man2html/2/malloc">malloc</A>
 -  memory allocator
@@ -548,7 +548,7 @@
 </DT>
 <DT><A HREF="/magic/man2html/2/string">string</A>
 -  extensible strings
-<DD><TT> s_alloc, s_append, s_array, s_copy, s_error, s_free, s_incref, s_memappend, s_nappend, s_new, s_newalloc, s_parse, s_reset, s_restart, s_terminate, s_tolower, s_putc, s_unique, s_grow, s_read, s_read_line, s_getline</TT>
+<DD><TT> s_alloc, s_append, s_array, s_copy, s_error, s_free, s_incref, s_memappend, s_nappend, s_new, s_newalloc, s_parse, s_reset, s_restart, s_terminate, s_tolower, s_putc, s_unique, s_grow, s_read, s_read_line, s_getline, s_allocinstack, s_freeinstack, s_rdinstack</TT>
 </DT>
 <DT><A HREF="/magic/man2html/2/stringsize">stringsize</A>
 -  graphical size of strings

+ 1 - 1
sys/man/3/INDEX.html

@@ -59,7 +59,7 @@
 <DD><TT> flash</TT>
 </DT>
 <DT><A HREF="/magic/man2html/3/floppy">floppy</A>
--  floppy disk interface
+-  floppy diskette interface
 <DD><TT> floppy</TT>
 </DT>
 <DT><A HREF="/magic/man2html/3/fs">fs</A>

+ 1 - 0
sys/man/4/INDEX

@@ -55,6 +55,7 @@ snapfs snap
 srv srv
 srvold9p srv
 srvssh srv
+ssh ssh
 32vfs tapefs
 cpiofs tapefs
 tapefs tapefs

+ 4 - 0
sys/man/4/INDEX.html

@@ -150,6 +150,10 @@
 -  start network file service
 <DD><TT> srv, srvold9p, 9fs, srvssh</TT>
 </DT>
+<DT><A HREF="/magic/man2html/4/ssh">ssh</A>
+-  serve SSH v2 over TCP
+<DD><TT> ssh, netssh</TT>
+</DT>
 <DT><A HREF="/magic/man2html/4/tapefs">tapefs</A>
 -  mount archival file systems
 <DD><TT> 32vfs, cpiofs, tapfs, tarfs, tpfs, v6fs, v10fs, zipfs</TT>

+ 535 - 0
sys/man/4/ssh

@@ -0,0 +1,535 @@
+.TH SSH 4
+.SH NAME
+ssh, netssh \- serve SSH v2 over TCP
+.SH SYNOPSIS
+.B netssh
+[
+.B -9dkv
+] [
+.B -m
+.I mntpt
+] [
+.B -s
+.I srvpt
+]
+.PP
+.2C
+.B "cd /net/ssh"
+.B ./clone
+.B ./ctl
+.B ./keys
+.BI ./ n
+.BI ./ n /clone
+.BI ./ n /ctl
+.BI ./ n /data
+.BI ./ n /listen
+.BI ./ n /local
+.BI ./ n /remote
+.BI ./ n /status
+.BI ./ n /tcp
+\&...
+.BI ./ n / ch
+.BI ./ n / ch /ctl
+.BI ./ n / ch /data
+.BI ./ n / ch /listen
+.BI ./ n / ch /request
+.BI ./ n / ch /status
+\&...
+.1C
+.fi
+.SH DESCRIPTION
+The
+.I netssh
+file server implements SSH v2 over TCP.
+All of the encryption, authentication, and SSH protocol are handled
+by a server managing SSH tunnels
+that appears as a protocol directory,
+.BR /net/ssh ,
+similar to those of
+.IR ip (3)
+but with an extra level of hierarchy for SSH channels within connections.
+Options are:
+.TF -m
+.TP
+.B -d
+Increase the amount of debugging output.
+.TP
+.B -k
+Use
+.IR keyfs (4)
+for password validation.
+.TP
+.B -m
+Mount point for the SSH protocol directory; defaults to
+.BR /net .
+.TP
+.B -s
+Name to post in
+.BR /srv .
+If
+.B -s
+is not given, no file is posted to
+.BR /srv .
+.TP
+.B -v
+Do not verify the remote system's host key (which is somewhat pedantic anyway).
+This lets us talk to SSH v1 systems.
+The presence of
+.B /env/nosshkeyverify
+is equivalent to specifying this option.
+.PD
+.LP
+.B /net/ssh
+contains a set of numbered directories, each of which
+is an SSH connection that is currently active or has been used in the past,
+per
+.IR ip (3).
+Opening
+.B clone
+reserves an SSH connection, reading from
+it returns the connection number reserved, and writing to it writes to the
+.B ctl
+file in the numbered connection directory.
+Reading the
+.B ctl
+file returns the most active state of any connection.
+.B /net/ssh/ctl
+currently implements no commands.
+Finally, the
+.B keys
+file is used by
+.IR ssh2 (1)
+to relay information about keys and passwords between a user and the SSH server.
+.LP
+Each of the numbered connection directories contains
+a set of numbered directories, one for each channel used on
+that connection (see
+.ft B
+Channel Directories
+.ft
+below).
+Similar to the top-level
+.B clone
+file, opening a connection's
+.B clone
+file reserves a channel and gives access to its
+.B ctl
+file.
+Reading from the
+.B ctl
+file returns the connection number (also the name of that directory).
+Commands may be written to a connection's
+.B ctl
+file:
+.TF connect
+.TP
+.B connect
+Dial the remote system and perform the initial
+handshake to exchange versions, lists of supported algorithms,
+and to establish the encryption keys to use.
+.TP
+.B ssh-userauth
+Attempt to authenticate a user with the remote system, with either
+public key authentication or a password.
+.TP
+.B ssh-connection
+Currently unsupported.
+.TP
+.B hangup
+Shut down a connection and all of its channels.
+.TP
+.B announce
+.B /net/ssh
+will accept connection requests from remote systems.
+.TP
+.B accept
+Do the initial connection handshake with the calling system.
+.TP
+.B reject
+Send back a connection rejection message to the caller
+and shut down the connection.
+.PD
+.LP
+Because data is always carried over a channel, the connection data file
+is not used for usual data.
+However, reads from the connection data file do return the capability
+needed for
+.I sshsession
+to change identity to the user logging in.
+As with other protocol directories, opens on
+.B listen
+block until a remote system establishes a connection, at which point,
+a server should write either an
+.B accept
+or
+.B reject
+message to the
+.B ctl
+file.
+The
+.B local
+and
+.B remote
+files give the IP addresses and port numbers of the local and remote
+systems.
+The connection
+.B status
+file gives the status of the most-recently established channel.
+.
+.SS "Channel Directories"
+In each channel directory,
+.BI /net/ssh/ conn / chan\fR,
+reads from channel
+.B ctl
+files return the channel number.
+Commands that may be written to a channel
+.B ctl
+file include:
+.TF connect
+.TP
+.B connect
+Create a new channel over this connection.
+SSHv2 defines
+.BR session ,
+.BR x11 ,
+.BR forwarded-tcpip ,
+and
+.B direct-tcpip
+channels.
+The
+.B connect
+command defaults to a
+.B session
+channel if no argument is given.
+(This implementation correctly handles only session channel requests.)
+.TP
+.B global
+Reserved for future development.
+In particular, this is necessary to support TCP/IP forwarding.
+.TP
+.B hangup
+Shut down a channel.
+If this is the last open channel on this connection, then shut down
+the connection too.
+.TP
+.B announce
+Announce willingness to accept new channel requests from the calling system.
+.PD
+.LP
+The channel
+.B data
+file is the file over which all application data is carried.
+Opens of the channel
+.B listen
+file block until a channel is opened by the remote end.
+Unlike the connection
+.B listen
+file, the listening program should not write an
+.B accept
+or
+.B reject
+message to the
+.B ctl
+file.
+.PP
+SSHv2 defines a number of out-of-band channel requests,
+sent and received through the
+.B request
+file.
+Among these are
+.BR env ,
+.BR exec ,
+.BR exit-signal ,
+.BR exit-status ,
+.BR pty-req ,
+.BR shell ,
+.BR signal ,
+.BR subsystem ,
+.BR window-change ,
+.BR x11-req ,
+and
+.BR xon-xoff .
+.I Sshsession
+only fully handles the
+.B shell
+and
+.B exec
+requests.
+Others are blithely acknowledged, rejected or ignored,
+depending on whether they are expected to be available by
+the remote system.
+.PP
+The channel
+.B status
+file contains one of:
+.BR Empty ,
+.BR Allocated ,
+.BR Initting ,
+.BR Listening ,
+.BR Opening ,
+.BR Negotiating ,
+.BR Authing ,
+.BR Established ,
+.BR Eof ,
+.BR Closing ,
+or
+.BR Closed .
+.
+.SS "Cryptographic Algorithms"
+During the initial connection exchange, both parties send lists of
+supported algorithms.
+The first list is for key exchange;
+we support
+.B diffie-hellman-group1-sha1
+and
+.BR diffie-hellman-group14-sha1 .
+The second list is the set for which corresponding host keys exist;
+we support
+.B ssh-rsa
+and
+.BR ssh-dss .
+The next lists are encryption algorithms, which may be negotiated
+independently for the server-to-client and client-to-server directions;
+we support
+.BR aes128-cbc ,
+.BR aes192-cbc ,
+.BR aes256-cbc ,
+.BR 3des-cbc ,
+and
+.B arcfour
+with preference given in that order.
+The final list is message authentication code algorithms;
+we only support
+.BR hmac-sha1 .
+.
+.SS "Keys and Management"
+Various keys are used by the SSH server.
+Most of them are expected to be stored in the instance of
+.IR factotum (4)
+running in the namespace of that server instance.
+Sometimes there are alternative locations.
+.LP
+The first key needed is the host key for server operation.
+In the case of the keys being stored in
+.IR factotum (4),
+these keys will be the first ones listed with
+.B proto=rsa
+and
+.BR proto=dss .
+Alternatively, these keys can be specified in the environment variables
+.B rsakey
+and
+.B dsskey
+or in files of the same names in the directory where
+.I netssh
+is started.
+.LP
+The next set of keys are the public host keys used by clients to
+verify the identities of servers.
+As with the original Plan 9 SSH implementation,
+there is a system-wide list of these in
+.B /sys/lib/ssh/keyring
+and each user may have a list in
+.BR $home/lib/keyring .
+If a public key for a remote server is listed and matches the one
+offered by the server, the connection proceeds.
+If a public key for a remote server is listed but does not match
+the one offered by the server, or
+if no public key is listed for a remote server,
+.I ssh
+(see
+.IR ssh2 (1))
+presents the key to the user and asks whether to reject the
+key, accept the key only for that session, or accept the key permanently.
+The last option causes the key to be written to the user's keyring.
+In the case of a mismatching key, the accept option can
+either be to add to or replace the old key.
+.LP
+An SSH daemon,
+such as
+.I sshsession
+in
+.IR ssh2 (1),
+must also have a list of public keys
+for public key authentication.
+Again, these keys must be stored in the
+.I factotum
+instance running in the name space of the daemon's SSH server.
+Each such key must have the attributes
+.BR role=verify,
+.BR proto=rsa ,
+and either
+.B user=
+or
+.BR sys= .
+.LP
+The next key is a user's private key to be used for public key authentication.
+We only support RSA keys for this, and the key must be in the
+.I factotum
+instance running in the namespace of the
+.IR ssh (4)
+instance.
+Creating a key and putting it in
+.I factotum
+can be done by:
+.IP
+.EX
+rsagen >key; read -m key >/mnt/factotum/ctl
+.EE
+.LP
+The key file will normally be loaded when
+.I factotum
+is started, either by way of
+.IR secstore (1)
+or directly in the user's
+.BR lib/profile .
+See
+.IR ssh (4)
+for
+.I rsa2ssh2
+(see
+.IR ssh2 (1)).
+.LP
+The command
+.IP
+.EX
+auth/pemdecode 'RSA PRIVATE KEY' id_rsa | auth/asn12rsa >key
+.EE
+.LP
+will translate a private key used with OpenSSH to one suitable
+for loading into
+.IR factotum .
+.LP
+To disambiguate when a user has more than one private key stored in
+.IR factotum ,
+the following selection criteria are applied:
+.TF "M."
+.PD 0.2v
+.TP 3
+1.
+The selected key must have both
+.B proto=rsa
+and
+.B !dk=
+attributes present.
+.TP
+2.
+Among those keys, the attributes
+.BR user= ,
+.BR sys= ,
+and any attribute/value pair specified in a
+.B -z
+option to
+.I ssh
+are examined.
+The value of the
+.B user
+attribute is expected to be the user name being authenticated on the remote
+system, and the value of the
+.B sys
+attribute is expected to be the remote system as specified in the
+.I ssh
+invocation.
+.TP
+3.
+The key with the greatest number of matches (most specific match) is selected.
+Among keys with equal number of matches, the first is chosen.
+.PD
+.LP
+For password-based user authentication,
+.I netssh
+can run in one of two modes.
+If given
+.BR -k ,
+it will validate passwords against those stored in
+.B /mnt/keys
+provided by
+.IR keyfs (4).
+If run without
+.BR -k ,
+it will validate passwords with an authentication server using
+.I auth_userpasswd
+in
+.IR auth (2).
+.SH FILES
+.TF /sys/lib/ssh/keyring
+.TP
+.B /sys/lib/ssh/keyring
+System-wide known host public keys.
+.TP
+.B $home/lib/keyring
+Per-user known host public keys.
+.TP
+.B /env/nosshkeyverify
+.SH SOURCE
+.B /sys/src/cmd/ssh2
+.SH "SEE ALSO"
+.IR ssh2 (1),
+.IR ip (3),
+.IR factotum (4),
+.IR keyfs (4),
+.IR authsrv (6),
+.IR dsa (8),
+.IR rsa (8)
+.br
+RFCs 4250, 4251, 4252, 4253, 4254, and 4419
+.SH BUGS
+.I Netssh
+shouldn't have to run as the host owner and using
+.IR factotum (4)
+correctly would permit this.
+.PP
+.I Netssh
+should be simplified by removing the top (connection) level of the
+.B /net/ssh
+hierarchy and multiplexing a single network connection
+after the initial negotiation.
+This would fit better with
+.IR dial (2),
+permit transports other than TCP,
+and allow co-existence of v1 and v2 implementations on a single TCP port.
+Better still,
+use only a single channel (since we don't use X11)
+and eliminate the top 2 levels.
+.PP
+.I Netssh
+authentication via
+.I keyfs
+and public keys uses
+.LR #¤/caphash ,
+which isn't normally present after
+.I cpurc
+runs, so
+.I netssh
+needs to be converted to use
+.IR factotum .
+.PP
+.B netssh
+.B -k
+assumes that
+.I keyfs
+is mounted,
+which is typically only true of authentication servers.
+.PP
+The
+.B keys
+file protocol should be documented.
+.PP
+Only capable of using TCP for underlying connections.
+.PP
+Can't coexist with SSH v1 on the same port.
+.PP
+Several aspects of key management still need some work.
+.PP
+TCP/IP forwarding and some potentially useful channel requests have not
+been implemented.
+.PP
+.B Zlib
+compression is not supported and probably not needed.
+.PP
+The SSH v2 protocol is a classic second system:
+over-engineered,
+overly complicated,
+misdesigned
+and
+jammed full of pointless goodies.

+ 6 - 0
sys/man/8/INDEX

@@ -47,6 +47,11 @@ diskparts diskparts
 dmaon diskparts
 disksim disksim
 drawterm drawterm
+asn12dsa dsa
+dsa dsa
+dsa2pub dsa
+dsa2ssh dsa
+dsagen dsa
 fossilcons fossilcons
 exsort fs
 fs fs
@@ -102,6 +107,7 @@ tcp7 listen
 tcp9 listen
 tcp993 listen
 tcp995 listen
+loadfossil loadfossil
 lp lp
 dump9660 mk9660
 mk9660 mk9660

+ 10 - 2
sys/man/8/INDEX.html

@@ -82,6 +82,10 @@
 -  connect to Plan 9 CPU servers from other operating systems
 <DD><TT> drawterm</TT>
 </DT>
+<DT><A HREF="/magic/man2html/8/dsa">dsa</A>
+-  generate and format dsa keys
+<DD><TT> dsagen, asn12dsa, dsa2pub, dsa2ssh</TT>
+</DT>
 <DT><A HREF="/magic/man2html/8/fossilcons">fossilcons</A>
 -  fossil console commands
 <DD><TT> fossilcons</TT>
@@ -134,6 +138,10 @@
 -  listen for calls on a network device
 <DD><TT> listen, listen1, tcp7, tcp9, tcp19, tcp21, tcp22, tcp23, tcp25, tcp53, tcp110, tcp113, tcp143, tcp513, tcp515, tcp564, tcp565, tcp566, tcp567, tcp993, tcp995, tcp1723, tcp17007, tcp17008, tcp17009, tcp17010, tcp17013</TT>
 </DT>
+<DT><A HREF="/magic/man2html/8/loadfossil">loadfossil</A>
+-  populate a fossil filesystem
+<DD><TT> loadfossil</TT>
+</DT>
 <DT><A HREF="/magic/man2html/8/lp">lp</A>
 -  PostScript preprocessors
 <DD><TT> lp</TT>
@@ -159,7 +167,7 @@
 <DD><TT> mksacfs</TT>
 </DT>
 <DT><A HREF="/magic/man2html/8/mkusbboot">mkusbboot</A>
--  generate bootable USB disk image
+-  generate bootable USB or other disk image for PC
 <DD><TT> mkusbboot</TT>
 </DT>
 <DT><A HREF="/magic/man2html/8/mouse">mouse</A>
@@ -203,7 +211,7 @@
 <DD><TT> ping, gping, traceroute, hogports</TT>
 </DT>
 <DT><A HREF="/magic/man2html/8/plan9.ini">plan9.ini</A>
--  configuration file for PCs
+-  configuration file primarily for PCs
 <DD><TT> plan9.ini</TT>
 </DT>
 <DT><A HREF="/magic/man2html/8/pop3">pop3</A>

File diff suppressed because it is too large
+ 0 - 0
sys/man/searchindex


+ 0 - 0
sys/src/cmd/ssh/agent.c → sys/src/cmd/ssh1/agent.c


+ 0 - 0
sys/src/cmd/ssh/authpasswd.c → sys/src/cmd/ssh1/authpasswd.c


+ 0 - 0
sys/src/cmd/ssh/authrsa.c → sys/src/cmd/ssh1/authrsa.c


+ 0 - 0
sys/src/cmd/ssh/authsrvpasswd.c → sys/src/cmd/ssh1/authsrvpasswd.c


+ 0 - 0
sys/src/cmd/ssh/authsrvtis.c → sys/src/cmd/ssh1/authsrvtis.c


+ 0 - 0
sys/src/cmd/ssh/authtis.c → sys/src/cmd/ssh1/authtis.c


+ 0 - 0
sys/src/cmd/ssh/cipher3des.c → sys/src/cmd/ssh1/cipher3des.c


+ 0 - 0
sys/src/cmd/ssh/cipherblowfish.c → sys/src/cmd/ssh1/cipherblowfish.c


+ 0 - 0
sys/src/cmd/ssh/cipherdes.c → sys/src/cmd/ssh1/cipherdes.c


+ 0 - 0
sys/src/cmd/ssh/ciphernone.c → sys/src/cmd/ssh1/ciphernone.c


+ 0 - 0
sys/src/cmd/ssh/cipherrc4.c → sys/src/cmd/ssh1/cipherrc4.c


+ 0 - 0
sys/src/cmd/ssh/ciphertwiddle.c → sys/src/cmd/ssh1/ciphertwiddle.c


+ 0 - 0
sys/src/cmd/ssh/cmsg.c → sys/src/cmd/ssh1/cmsg.c


+ 2 - 2
sys/src/cmd/ssh/mkfile → sys/src/cmd/ssh1/mkfile

@@ -4,7 +4,7 @@ HFILES=ssh.h
 
 TARG=\
 	scp\
-	ssh\
+	ssh1\
 	sshnet\
 	sshserve\
 
@@ -46,7 +46,7 @@ UPDATE=\
 
 </sys/src/cmd/mkmany
 
-$O.ssh: \
+$O.ssh1: \
 	$AUTHOFILES\
 	$CIPHEROFILES\
 	agent.$O\

+ 0 - 0
sys/src/cmd/ssh/msg.c → sys/src/cmd/ssh1/msg.c


+ 0 - 0
sys/src/cmd/ssh/pubkey.c → sys/src/cmd/ssh1/pubkey.c


+ 0 - 0
sys/src/cmd/ssh/scp.c → sys/src/cmd/ssh1/scp.c


+ 0 - 0
sys/src/cmd/ssh/smsg.c → sys/src/cmd/ssh1/smsg.c


+ 0 - 0
sys/src/cmd/ssh/ssh.h → sys/src/cmd/ssh1/ssh.h


+ 1 - 0
sys/src/cmd/ssh/ssh.c → sys/src/cmd/ssh1/ssh1.c

@@ -1,3 +1,4 @@
+/* remote login via ssh v1 */
 #include "ssh.h"
 
 int cooked = 0;		/* user wants cooked mode */

+ 0 - 0
sys/src/cmd/ssh/sshnet.c → sys/src/cmd/ssh1/sshnet.c


+ 0 - 0
sys/src/cmd/ssh/sshserve.c → sys/src/cmd/ssh1/sshserve.c


+ 0 - 0
sys/src/cmd/ssh/util.c → sys/src/cmd/ssh1/util.c


+ 27 - 0
sys/src/cmd/ssh2/_changes

@@ -0,0 +1,27 @@
+still work to be done!
+but the worst maintainability problems are fixed.
+
+work still to be done (not exhaustive):
+use dial(2) routines instead of manual diddling
+use factotum and auth_* routines
+
+fixed bugs
+	systemic bugs like overflow at both ends in
+		n = read(n, buf, sizeof buf);
+		buf[n] = 0;
+	fixed unchecked malloc calls
+replaced many magic numbers with named constants
+	see magic.out for remainder
+duplicated code broken out into functions
+	see dup.* for remaining offenders
+	use readio & writeio throughout
+big functions broken up
+	see long.funcs for remaining offenders
+converted to plan 9 style
+	*File -> Q* per all other file servers
+	very long lines wrapped for finite-width devices
+use syslog in daemons
+added /net/ssh/*/tcp containing number of the underlying tcp connection
+	originally for sshswitch, see _coexistence-is-futile
+edited manual page for clarity and brevity, split in two
+worked out problems with normal usage cases, fixed or documented them

+ 49 - 0
sys/src/cmd/ssh2/cipher3des.c

@@ -0,0 +1,49 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <libsec.h>
+#include "netssh.h"
+
+struct CipherState {
+	DES3state state;
+};
+
+static CipherState*
+init3des(Conn *c, int dir)
+{
+	CipherState *cs;
+	uchar key[3][8];
+
+	cs = emalloc9p(sizeof(CipherState));
+	if(dir){
+		memmove(key, c->s2cek, sizeof key);
+		setupDES3state(&cs->state, key, c->s2civ);
+	} else {
+		memmove(key, c->c2sek, sizeof key);
+		setupDES3state(&cs->state, key, c->c2siv);
+	}
+	return cs;
+}
+
+static void
+encrypt3des(CipherState *cs, uchar *buf, int nbuf)
+{
+	des3CBCencrypt(buf, nbuf, &cs->state);
+}
+
+static void
+decrypt3des(CipherState *cs, uchar *buf, int nbuf)
+{
+	des3CBCdecrypt(buf, nbuf, &cs->state);
+}
+
+Cipher cipher3des = {
+	"3des-cbc",
+	8,
+	init3des,
+	encrypt3des,
+	decrypt3des,
+};

+ 91 - 0
sys/src/cmd/ssh2/cipheraes.c

@@ -0,0 +1,91 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <libsec.h>
+#include "netssh.h"
+
+static QLock aeslock;
+
+struct CipherState {
+	AESstate state;
+};
+
+static CipherState *
+initaes(Conn *c, int dir, int bits)
+{
+	CipherState *cs;
+
+	qlock(&aeslock);
+	cs = emalloc9p(sizeof(CipherState));
+	if(dir)
+		setupAESstate(&cs->state, c->s2cek, bits/8, c->s2civ);
+	else
+		setupAESstate(&cs->state, c->c2sek, bits/8, c->c2siv);
+	qunlock(&aeslock);
+	return cs;
+}
+
+static CipherState*
+initaes128(Conn *c, int dir)
+{
+	return initaes(c, dir, 128);
+}
+
+static CipherState*
+initaes192(Conn *c, int dir)
+{
+	return initaes(c, dir, 192);
+}
+
+static CipherState*
+initaes256(Conn *c, int dir)
+{
+	return initaes(c, dir, 256);
+}
+
+static void
+encryptaes(CipherState *cs, uchar *buf, int nbuf)
+{
+	if(cs->state.setup != 0xcafebabe || cs->state.rounds > AESmaxrounds)
+		return;
+	qlock(&aeslock);
+	aesCBCencrypt(buf, nbuf, &cs->state);
+	qunlock(&aeslock);
+}
+
+static void
+decryptaes(CipherState *cs, uchar *buf, int nbuf)
+{
+	if(cs->state.setup != 0xcafebabe || cs->state.rounds > AESmaxrounds)
+		return;
+	qlock(&aeslock);
+	aesCBCdecrypt(buf, nbuf, &cs->state);
+	qunlock(&aeslock);
+}
+
+Cipher cipheraes128 = {
+	"aes128-cbc",
+	AESbsize,
+	initaes128,
+	encryptaes,
+	decryptaes,
+};
+
+Cipher cipheraes192 = {
+	"aes192-cbc",
+	AESbsize,
+	initaes192,
+	encryptaes,
+	decryptaes,
+};
+
+Cipher cipheraes256 = {
+	"aes256-cbc",
+	AESbsize,
+	initaes256,
+	encryptaes,
+	decryptaes,
+};

+ 66 - 0
sys/src/cmd/ssh2/cipherblowfish.c

@@ -0,0 +1,66 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <libsec.h>
+#include "netssh.h"
+
+struct CipherState {
+	BFstate	state;
+};
+
+static CipherState*
+initblowfish(Conn *c, int dir)
+{
+	int i;
+	CipherState *cs;
+
+	if (debug > 1) {
+		fprint(2, "initblowfish dir:%d\ns2cek: ", dir);
+		for(i = 0; i < 16; ++i)
+			fprint(2, "%02x", c->s2cek[i]);
+		fprint(2, "\nc2sek: ");
+		for(i = 0; i < 16; ++i)
+			fprint(2, "%02x", c->c2sek[i]);
+		fprint(2, "\ns2civ: ");
+		for(i = 0; i < 8; ++i)
+			fprint(2, "%02x", c->s2civ[i]);
+		fprint(2, "\nc2siv: ");
+		for(i = 0; i < 8; ++i)
+			fprint(2, "%02x", c->c2siv[i]);
+		fprint(2, "\n");
+	}
+	cs = emalloc9p(sizeof(CipherState));
+	memset(cs, '\0', sizeof *cs);
+	fprint(2, "cs: %p\n", cs);
+	if(dir)
+		setupBFstate(&cs->state, c->s2cek, 16, c->s2civ);
+	else
+		setupBFstate(&cs->state, c->c2sek, 16, c->c2siv);
+	return cs;
+}
+
+static void
+encryptblowfish(CipherState *cs, uchar *buf, int nbuf)
+{
+	bfCBCencrypt(buf, nbuf, &cs->state);
+}
+
+static void
+decryptblowfish(CipherState *cs, uchar *buf, int nbuf)
+{
+fprint(2, "cs: %p, nb:%d\n", cs, nbuf);
+fprint(2, "before decrypt: %02ux %02ux %02ux %02ux\n", buf[0], buf[1], buf[2], buf[3]);
+	bfCBCdecrypt(buf, nbuf, &cs->state);
+fprint(2, "after decrypt: %02ux %02ux %02ux %02ux\n", buf[0], buf[1], buf[2], buf[3]);
+}
+
+Cipher cipherblowfish = {
+	"blowfish-cbc",
+	8,
+	initblowfish,
+	encryptblowfish,
+	decryptblowfish,
+};

+ 46 - 0
sys/src/cmd/ssh2/cipherrc4.c

@@ -0,0 +1,46 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <libsec.h>
+#include "netssh.h"
+
+struct CipherState {
+	RC4state state;
+};
+
+static CipherState*
+initrc4(Conn *c, int dir)
+{
+	CipherState *cs;
+
+	cs = emalloc9p(sizeof(CipherState));
+	if(dir)
+		setupRC4state(&cs->state, c->s2cek, 16);
+	else
+		setupRC4state(&cs->state, c->c2sek, 16);
+	return cs;
+}
+
+static void
+encryptrc4(CipherState *cs, uchar *buf, int nbuf)
+{
+	rc4(&cs->state, buf, nbuf);
+}
+
+static void
+decryptrc4(CipherState *cs, uchar *buf, int nbuf)
+{
+	rc4(&cs->state, buf, nbuf);
+}
+
+Cipher cipherrc4 = {
+	"arcfour",
+	8,
+	initrc4,
+	encryptrc4,
+	decryptrc4,
+};
+

+ 30 - 0
sys/src/cmd/ssh2/common.c

@@ -0,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+#include "ssh2.h"
+
+void
+freeptr(void **vpp)
+{
+	char **cpp;
+
+	cpp = vpp;
+	free(*cpp);
+	*cpp = nil;
+}
+
+int
+readfile(char *file, char *buf, int size)
+{
+	int n, fd;
+
+	fd = open(file, OREAD);
+	if (fd < 0)
+		return -1;
+	n = readn(fd, buf, size - 1);
+	if (n < 0)
+		buf[0] = '\0';
+	else
+		buf[n] = '\0';
+	close(fd);
+	return n;
+}

+ 930 - 0
sys/src/cmd/ssh2/dh.c

@@ -0,0 +1,930 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <auth.h>
+#include <ip.h>
+#include <pool.h>
+#include "netssh.h"
+
+static int dh_server(Conn *, Packet *, mpint *, int);
+static void genkeys(Conn *, uchar [], mpint *);
+
+/*
+ * Second Oakley Group from RFC 2409
+ */
+static char *group1p =
+         "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+         "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+         "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+         "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+         "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+         "FFFFFFFFFFFFFFFF";
+
+/*
+ * 2048-bit MODP group (id 14) from RFC 3526
+*/
+static char *group14p =
+      "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+      "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+      "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+      "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+      "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+      "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+      "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+      "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+      "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+      "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+      "15728E5A8AACAA68FFFFFFFFFFFFFFFF";
+
+mpint *two, *p1, *p14;
+int nokeyverify;
+
+static DSApriv mydsskey;
+static RSApriv myrsakey;
+
+void
+dh_init(PKA *pkas[])
+{
+	char *buf, *p, *st, *end;
+	int fd, n, k;
+
+	if(debug > 1)
+		sshdebug(nil, "dh_init");
+	k = 0;
+	pkas[k] = nil;
+	fmtinstall('M', mpfmt);
+	two = strtomp("2", nil, 10, nil);
+	p1 = strtomp(group1p, nil, 16, nil);
+	p14 = strtomp(group14p, nil, 16, nil);
+
+	/*
+	 * this really should be done through factotum
+	 */
+	p = getenv("rsakey");
+	if (p != nil) {
+		remove("/env/rsakey");
+		st = buf = p;
+		end = buf + strlen(p);
+	} else {
+		/*
+		 * it would be better to use bio and rdline here instead of
+		 * reading all of factotum's contents into memory at once.
+		 */
+		buf = emalloc9p(Maxfactotum);
+		fd = open("rsakey", OREAD);
+		if (fd < 0 && (fd = open("/mnt/factotum/ctl", OREAD)) < 0)
+			goto norsa;
+		n = readn(fd, buf, Maxfactotum - 1);
+		buf[n >= 0? n: 0] = 0;
+		close(fd);
+		assert(n < Maxfactotum - 1);
+
+		st = strstr(buf, "proto=rsa");
+		if (st == nil) {
+			sshlog(nil, "no proto=rsa key in factotum");
+			goto norsa;
+		}
+		end = st;
+		for (; st > buf && *st != '\n'; --st)
+			;
+		for (; end < buf + Maxfactotum && *end != '\n'; ++end)
+			;
+	}
+	p = strstr(st, " n=");
+	if (p == nil || p > end) {
+		sshlog(nil, "no key (n) found");
+		free(buf);
+		return;
+	}
+	myrsakey.pub.n = strtomp(p+3, nil, 16, nil);
+	if (debug > 1)
+		sshdebug(nil, "n=%M", myrsakey.pub.n);
+	p = strstr(st, " ek=");
+	if (p == nil || p > end) {
+		sshlog(nil, "no key (ek) found");
+		free(buf);
+		return;
+	}
+	pkas[k++] = &rsa_pka;
+	pkas[k] = nil;
+	myrsakey.pub.ek = strtomp(p+4, nil, 16, nil);
+	if (debug > 1)
+		sshdebug(nil, "ek=%M", myrsakey.pub.ek);
+	p = strstr(st, " !dk=");
+	if (p == nil) {
+		p = strstr(st, "!dk?");
+		if (p == nil || p > end) {
+			// sshlog(nil, "no key (dk) found");
+			free(buf);
+			return;
+		}
+		goto norsa;
+	}
+	myrsakey.dk = strtomp(p+5, nil, 16, nil);
+	if (debug > 1)
+		sshdebug(nil, "dk=%M", myrsakey.dk);
+norsa:
+	free(buf);
+
+	p = getenv("dsskey");
+	if (p != nil) {
+		remove("/env/dsskey");
+		buf = p;
+		end = buf + strlen(p);
+	} else {
+		/*
+		 * it would be better to use bio and rdline here instead of
+		 * reading all of factotum's contents into memory at once.
+		 */
+		buf = emalloc9p(Maxfactotum);
+		fd = open("dsskey", OREAD);
+		if (fd < 0 && (fd = open("/mnt/factotum/ctl", OREAD)) < 0)
+			return;
+		n = readn(fd, buf, Maxfactotum - 1);
+		buf[n >= 0? n: 0] = 0;
+		close(fd);
+		assert(n < Maxfactotum - 1);
+
+		st = strstr(buf, "proto=dsa");
+		if (st == nil) {
+			sshlog(nil, "no proto=dsa key in factotum");
+			free(buf);
+			return;
+		}
+		end = st;
+		for (; st > buf && *st != '\n'; --st)
+			;
+		for (; end < buf + Maxfactotum && *end != '\n'; ++end)
+			;
+	}
+	p = strstr(buf, " p=");
+	if (p == nil || p > end) {
+		sshlog(nil, "no key (p) found");
+		free(buf);
+		return;
+	}
+	mydsskey.pub.p = strtomp(p+3, nil, 16, nil);
+	p = strstr(buf, " q=");
+	if (p == nil || p > end) {
+		sshlog(nil, "no key (q) found");
+		free(buf);
+		return;
+	}
+	mydsskey.pub.q = strtomp(p+3, nil, 16, nil);
+	p = strstr(buf, " alpha=");
+	if (p == nil || p > end) {
+		sshlog(nil, "no key (g) found");
+		free(buf);
+		return;
+	}
+	mydsskey.pub.alpha = strtomp(p+7, nil, 16, nil);
+	p = strstr(buf, " key=");
+	if (p == nil || p > end) {
+		sshlog(nil, "no key (y) found");
+		free(buf);
+		return;
+	}
+	mydsskey.pub.key = strtomp(p+5, nil, 16, nil);
+	pkas[k++] = &dss_pka;
+	pkas[k] = nil;
+	p = strstr(buf, " !secret=");
+	if (p == nil) {
+		p = strstr(buf, "!secret?");
+		if (p == nil || p > end)
+			sshlog(nil, "no key (x) found");
+		free(buf);
+		return;
+	}
+	mydsskey.secret = strtomp(p+9, nil, 16, nil);
+	free(buf);
+}
+
+static Packet *
+rsa_ks(Conn *c)
+{
+	Packet *ks;
+
+	if (myrsakey.pub.ek == nil || myrsakey.pub.n == nil) {
+		sshlog(c, "no public RSA key info");
+		return nil;
+	}
+	ks = new_packet(c);
+	add_string(ks, "ssh-rsa");
+	add_mp(ks, myrsakey.pub.ek);
+	add_mp(ks, myrsakey.pub.n);
+	return ks;
+}
+
+static void
+esma_encode(uchar *h, uchar *em, int nb)
+{
+	int n, i;
+	uchar hh[SHA1dlen];
+	static uchar sha1der[] = {
+		0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
+		0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
+	};
+
+	sha1(h, SHA1dlen, hh, nil);
+	n = nb - (nelem(sha1der) + SHA1dlen) - 3;
+	i = 0;
+	em[i++] = 0;
+	em[i++] = 1;
+	memset(em + i, 0xff, n);
+	i += n;
+	em[i++] = 0;
+	memmove(em + i, sha1der, sizeof sha1der);
+	i += sizeof sha1der;
+	memmove(em + i, hh, SHA1dlen);
+}
+
+static Packet *
+rsa_sign(Conn *c, uchar *m, int nm)
+{
+	AuthRpc *ar;
+	Packet *sig;
+	mpint *s, *mm;
+	int fd, n, nbit;
+	uchar hh[SHA1dlen];
+	uchar *sstr, *em;
+
+	if (myrsakey.dk) {
+		nbit = mpsignif (myrsakey.pub.n);
+		n = (nbit + 7) / 8;
+		sstr = emalloc9p(n);
+		em = emalloc9p(n);
+		/* Compute s: RFC 3447 */
+		esma_encode(m, em, n);
+		mm = betomp(em, n, nil);
+		s = mpnew(nbit);
+		mpexp(mm, myrsakey.dk, myrsakey.pub.n, s);
+		mptobe(s, sstr, n, nil);
+		mpfree(mm);
+		mpfree(s);
+		free(em);
+	} else {
+		fd = open("/mnt/factotum/rpc", ORDWR);
+		if (fd < 0)
+			return nil;
+		sha1(m, nm, hh, nil);
+		ar = auth_allocrpc(fd);
+		if (ar == nil ||
+		    auth_rpc(ar, "start", "role=sign proto=rsa", 19) != ARok ||
+		    auth_rpc(ar, "write", hh, SHA1dlen) != ARok ||
+		    auth_rpc(ar, "read", nil, 0) != ARok ||
+		    ar->arg == nil) {
+			sshdebug(c, "got error in factotum: %r");
+			auth_freerpc(ar);
+			close(fd);
+			return nil;
+		}
+		sstr = emalloc9p(ar->narg);
+		memmove(sstr, ar->arg, ar->narg);
+		n = ar->narg;
+
+		auth_freerpc(ar);
+		close(fd);
+	}
+	sig = new_packet(c);
+	add_string(sig, pkas[c->pkalg]->name);
+	add_block(sig, sstr, n);
+	free(sstr);
+	return sig;
+}
+
+/*
+ * 0 - If factotum failed, e.g. no key
+ * 1 - If key is verified
+ * -1 - If factotum found a key, but the verification fails
+ */
+static int
+rsa_verify(Conn *c, uchar *m, int nm, char *user, char *sig, int)
+{
+	int fd, n, retval, nbit;
+	char *buf, *p, *sigblob;
+	uchar *sstr, *em;
+	uchar hh[SHA1dlen];
+	mpint *s, *mm, *rsa_exponent, *host_modulus;
+	AuthRpc *ar;
+
+	sshdebug(c, "in rsa_verify for connection: %d", c->id);
+	SET(rsa_exponent, host_modulus);
+	USED(rsa_exponent, host_modulus);
+	if (0 && rsa_exponent) {
+		nbit = mpsignif(host_modulus);
+		n = (nbit + 7) / 8;
+		em = emalloc9p(n);
+		/* Compute s: RFC 3447 */
+		esma_encode(m, em, n);
+		mm = betomp(em, n, nil);
+		s = mpnew(1024);
+		mpexp(mm, rsa_exponent, host_modulus, s);
+		sstr = emalloc9p(n);
+		mptobe(s, sstr, n, nil);
+		free(em);
+		mpfree(mm);
+		mpfree(s);
+		retval = memcmp(sig, sstr, n);
+		free(sstr);
+		retval = (retval == 0);
+	} else {
+		retval = 1;
+		fd = open("/mnt/factotum/rpc", ORDWR);
+		if (fd < 0) {
+			sshdebug(c, "could not open factotum RPC: %r");
+			return 0;
+		}
+		buf = emalloc9p(Blobsz / 2);
+		sigblob = emalloc9p(Blobsz);
+		p = (char *)get_string(nil, (uchar *)sig, buf, Blobsz / 2, nil);
+		get_string(nil, (uchar *)p, sigblob, Blobsz, &n);
+		sha1(m, nm, hh, nil);
+		if (user != nil)
+			p = smprint("role=verify proto=rsa user=%s", user);
+		else
+			p = smprint("role=verify proto=rsa sys=%s", c->remote);
+
+		ar = auth_allocrpc(fd);
+		if (ar == nil || auth_rpc(ar, "start", p, strlen(p)) != ARok ||
+		    auth_rpc(ar, "write", hh, SHA1dlen) != ARok ||
+		    auth_rpc(ar, "write", sigblob, n) != ARok ||
+		    auth_rpc(ar, "read", nil, 0) != ARok) {
+			sshdebug(c, "got error in factotum: %r");
+			retval = 0;
+		} else {
+			sshdebug(c, "factotum returned %s", ar->ibuf);
+			if (strstr(ar->ibuf, "does not verify") != nil)
+				retval = -1;
+		}
+		if (ar != nil)
+			auth_freerpc(ar);
+		free(p);
+		close(fd);
+		free(sigblob);
+		free(buf);
+	}
+	return retval;
+}
+
+static Packet *
+dss_ks(Conn *c)
+{
+	Packet *ks;
+
+	if (mydsskey.pub.p == nil)
+		return nil;
+	ks = new_packet(c);
+	add_string(ks, "ssh-dss");
+	add_mp(ks, mydsskey.pub.p);
+	add_mp(ks, mydsskey.pub.q);
+	add_mp(ks, mydsskey.pub.alpha);
+	add_mp(ks, mydsskey.pub.key);
+	return ks;
+}
+
+static Packet *
+dss_sign(Conn *c, uchar *m, int nm)
+{
+	AuthRpc *ar;
+	DSAsig *s;
+	Packet *sig;
+	mpint *mm;
+	int fd;
+	uchar sstr[2*SHA1dlen];
+
+	sha1(m, nm, sstr, nil);
+	sig = new_packet(c);
+	add_string(sig, pkas[c->pkalg]->name);
+	if (mydsskey.secret) {
+		mm = betomp(sstr, SHA1dlen, nil);
+		s = dsasign(&mydsskey, mm);
+		mptobe(s->r, sstr, SHA1dlen, nil);
+		mptobe(s->s, sstr+SHA1dlen, SHA1dlen, nil);
+		dsasigfree(s);
+		mpfree(mm);
+	} else {
+		fd = open("/mnt/factotum/rpc", ORDWR);
+		if (fd < 0)
+			return nil;
+		ar = auth_allocrpc(fd);
+		if (ar == nil ||
+		    auth_rpc(ar, "start", "role=sign proto=dsa", 19) != ARok ||
+		    auth_rpc(ar, "write", sstr, SHA1dlen) != ARok ||
+		    auth_rpc(ar, "read", nil, 0) != ARok) {
+			sshdebug(c, "got error in factotum: %r");
+			auth_freerpc(ar);
+			close(fd);
+			return nil;
+		}
+		memmove(sstr, ar->arg, ar->narg);
+		auth_freerpc(ar);
+		close(fd);
+	}
+	add_block(sig, sstr, 2*SHA1dlen);
+	return sig;
+}
+
+static int
+dss_verify(Conn *c, uchar *m, int nm, char *user, char *sig, int nsig)
+{
+	sshdebug(c, "in dss_verify for connection: %d", c->id);
+	USED(m, nm, user, sig, nsig);
+	return 0;
+}
+
+static int
+dh_server1(Conn *c, Packet *pack1)
+{
+	return dh_server(c, pack1, p1, 1024);
+}
+
+static int
+dh_server14(Conn *c, Packet *pack1)
+{
+	return dh_server(c, pack1, p14, 2048);
+}
+
+static int
+dh_server(Conn *c, Packet *pack1, mpint *grp, int nbit)
+{
+	Packet *pack2, *ks, *sig;
+	mpint *y, *e, *f, *k;
+	int n, ret;
+	uchar h[SHA1dlen];
+
+	ret = -1;
+	qlock(&c->l);
+	f = mpnew(nbit);
+	k = mpnew(nbit);
+
+	/* Compute f: RFC4253 */
+	y = mprand(nbit / 8, genrandom, nil);
+	if (debug > 1)
+		sshdebug(c, "y=%M", y);
+	mpexp(two, y, grp, f);
+	if (debug > 1)
+		sshdebug(c, "f=%M", f);
+
+	/* Compute k: RFC4253 */
+	if (debug > 1)
+		dump_packet(pack1);
+	e = get_mp(pack1->payload+1);
+	if (debug > 1)
+		sshdebug(c, "e=%M", e);
+	mpexp(e, y, grp, k);
+	if (debug > 1)
+		sshdebug(c, "k=%M", k);
+
+	/* Compute H: RFC 4253 */
+	pack2 = new_packet(c);
+	sshdebug(c, "ID strings: %s---%s", c->otherid, MYID);
+	add_string(pack2, c->otherid);
+	add_string(pack2, MYID);
+	if (debug > 1) {
+		fprint(2, "received kexinit:");
+		dump_packet(c->rkexinit);
+		fprint(2, "\nsent kexinit:");
+		dump_packet(c->skexinit);
+	}
+	add_block(pack2, c->rkexinit->payload, c->rkexinit->rlength - 1);
+	add_block(pack2, c->skexinit->payload,
+		c->skexinit->rlength - c->skexinit->pad_len - 1);
+	sig = nil;
+	ks = pkas[c->pkalg]->ks(c);
+	if (ks == nil)
+		goto err;
+	add_block(pack2, ks->payload, ks->rlength - 1);
+	add_mp(pack2, e);
+	add_mp(pack2, f);
+	add_mp(pack2, k);
+	sha1(pack2->payload, pack2->rlength - 1, h, nil);
+
+	if (c->got_sessid == 0) {
+		memmove(c->sessid, h, SHA1dlen);
+		c->got_sessid = 1;
+	}
+	sig = pkas[c->pkalg]->sign(c, h, SHA1dlen);
+	if (sig == nil) {
+		sshlog(c, "failed to generate signature: %r");
+		goto err;
+	}
+
+	/* Send (K_s || f || s) to client: RFC4253 */
+	init_packet(pack2);
+	pack2->c = c;
+	add_byte(pack2, SSH_MSG_KEXDH_REPLY);
+	add_block(pack2, ks->payload, ks->rlength - 1);
+	add_mp(pack2, f);
+	add_block(pack2, sig->payload, sig->rlength - 1);
+	if (debug > 1)
+		dump_packet(pack2);
+	n = finish_packet(pack2);
+	if (debug > 1) {
+		sshdebug(c, "writing %d bytes: len %d", n, nhgetl(pack2->nlength));
+		dump_packet(pack2);
+	}
+	iowrite(c->dio, c->datafd, pack2->nlength, n);
+
+	genkeys(c, h, k);
+
+	/* Send SSH_MSG_NEWKEYS */
+	init_packet(pack2);
+	pack2->c = c;
+	add_byte(pack2, SSH_MSG_NEWKEYS);
+	n = finish_packet(pack2);
+	iowrite(c->dio, c->datafd, pack2->nlength, n);
+	ret = 0;
+err:
+	mpfree(f);
+	mpfree(e);
+	mpfree(k);
+	mpfree(y);
+	free(sig);
+	free(ks);
+	free(pack2);
+	qunlock(&c->l);
+	return ret;
+}
+
+static int
+dh_client11(Conn *c, Packet *)
+{
+	Packet *p;
+	int n;
+
+	if (c->e)
+		mpfree(c->e);
+	c->e = mpnew(1024);
+
+	/* Compute e: RFC4253 */
+	if (c->x)
+		mpfree(c->x);
+	c->x = mprand(128, genrandom, nil);
+	mpexp(two, c->x, p1, c->e);
+
+	p = new_packet(c);
+	add_byte(p, SSH_MSG_KEXDH_INIT);
+	add_mp(p, c->e);
+	n = finish_packet(p);
+	iowrite(c->dio, c->datafd, p->nlength, n);
+	free(p);
+	return 0;
+}
+
+static int
+findkeyinuserring(Conn *c, RSApub *srvkey)
+{
+	int n;
+	char *home, *newkey, *r;
+
+	home = getenv("home");
+	if (home == nil) {
+		newkey = "No home directory for key file";
+		free(keymbox.msg);
+		keymbox.msg = smprint("b%04ld%s", strlen(newkey), newkey);
+		return -1;
+	}
+
+	r = smprint("%s/lib/keyring", home);
+	free(home);
+	if ((n = findkey(r, c->remote, srvkey)) != KeyOk) {
+		newkey = smprint("ek=%M n=%M", srvkey->ek, srvkey->n);
+		free(keymbox.msg);
+		keymbox.msg = smprint("%c%04ld%s", n == NoKeyFile || n == NoKey?
+			'c': 'b', strlen(newkey), newkey);
+		free(newkey);
+
+		nbsendul(keymbox.mchan, 1);
+		recvul(keymbox.mchan);
+		if (keymbox.msg == nil || keymbox.msg[0] == 'n') {
+			free(keymbox.msg);
+			keymbox.msg = nil;
+			newkey = "Server key reject";
+			keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey);
+			return -1;
+		}
+		sshdebug(c, "adding key");
+		if (keymbox.msg[0] == 'y')
+			appendkey(r, c->remote, srvkey);
+		else if (keymbox.msg[0] == 'r')
+			replacekey(r, c->remote, srvkey);
+	}
+	free(r);
+	return 0;
+}
+
+static int
+verifyhostkey(Conn *c, RSApub *srvkey, Packet *sig)
+{
+	int fd, n;
+	char *newkey;
+	uchar h[SHA1dlen];
+
+	sshdebug(c, "verifying server signature");
+	if (findkey("/sys/lib/ssh/keyring", c->remote, srvkey) != KeyOk &&
+	    findkeyinuserring(c, srvkey) < 0) {
+		nbsendul(keymbox.mchan, 1);
+		mpfree(srvkey->ek);
+		mpfree(srvkey->n);
+		return -2;
+	}
+
+	newkey = smprint("key proto=rsa role=verify sys=%s size=%d ek=%M n=%M",
+		c->remote, mpsignif(srvkey->n), srvkey->ek, srvkey->n);
+	if (newkey == nil) {
+		sshlog(c, "out of memory");
+		exits("out of memory");
+	}
+
+	fd = open("/mnt/factotum/ctl", OWRITE);
+	if (fd >= 0)
+		write(fd, newkey, strlen(newkey));
+		/* leave fd open */
+	else
+		sshdebug(c, "factotum open failed: %r");
+
+	free(newkey);
+	mpfree(srvkey->ek);
+	mpfree(srvkey->n);
+	free(keymbox.msg);
+	keymbox.msg = nil;
+
+	n = pkas[c->pkalg]->verify(c, h, SHA1dlen, nil, (char *)sig->payload,
+		sig->rlength);
+
+	/* fd is perhaps still open */
+	if (fd >= 0) {
+		/* sys here is a dotted-quad ip address */
+		newkey = smprint("delkey proto=rsa role=verify sys=%s",
+			c->remote);
+		if (newkey) {
+			seek(fd, 0, 0);
+			write(fd, newkey, strlen(newkey));
+			free(newkey);
+		}
+		close(fd);
+	}
+	return n;
+}
+
+static int
+dh_client12(Conn *c, Packet *p)
+{
+	int n, retval;
+	char *newkey;
+	char buf[10];
+	uchar *q;
+	uchar h[SHA1dlen];
+	mpint *f, *k;
+	Packet *ks, *sig, *pack2;
+	RSApub *srvkey;
+
+	retval = -1;
+	ks = new_packet(c);
+	sig = new_packet(c);
+	pack2 = new_packet(c);
+
+	q = get_string(p, p->payload+1, (char *)ks->payload, Maxpktpay, &n);
+	ks->rlength = n + 1;
+	f = get_mp(q);
+	q += nhgetl(q) + 4;
+	get_string(p, q, (char *)sig->payload, Maxpktpay, &n);
+	sig->rlength = n;
+	k = mpnew(1024);
+	mpexp(f, c->x, p1, k);
+
+	/* Compute H: RFC 4253 */
+	init_packet(pack2);
+	pack2->c = c;
+	if (debug > 1)
+		sshdebug(c, "ID strings: %s---%s", c->otherid, MYID);
+	add_string(pack2, MYID);
+	add_string(pack2, c->otherid);
+	if (debug > 1) {
+		fprint(2, "received kexinit:");
+		dump_packet(c->rkexinit);
+		fprint(2, "\nsent kexinit:");
+		dump_packet(c->skexinit);
+	}
+	add_block(pack2, c->skexinit->payload,
+		c->skexinit->rlength - c->skexinit->pad_len - 1);
+	add_block(pack2, c->rkexinit->payload, c->rkexinit->rlength - 1);
+	add_block(pack2, ks->payload, ks->rlength - 1);
+	add_mp(pack2, c->e);
+	add_mp(pack2, f);
+	add_mp(pack2, k);
+	sha1(pack2->payload, pack2->rlength - 1, h, nil);
+	mpfree(f);
+
+	if (c->got_sessid == 0) {
+		memmove(c->sessid, h, SHA1dlen);
+		c->got_sessid = 1;
+	}
+
+	q = get_string(ks, ks->payload, buf, sizeof buf, nil);
+	srvkey = emalloc9p(sizeof (RSApub));
+	srvkey->ek = get_mp(q);
+	q += nhgetl(q) + 4;
+	srvkey->n = get_mp(q);
+
+	/*
+	 * key verification is really pretty pedantic and
+	 * not doing it lets us talk to ssh v1 implementations.
+	 */
+	if (nokeyverify)
+		n = 1;
+	else
+		n = verifyhostkey(c, srvkey, sig);
+	switch (n) {
+	case -2:
+		goto out;
+	case -1:
+		newkey = "Signature verification failed; try ssh -v";
+		keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey);
+		break;
+	case 0:
+		newkey = "Key verification dialog failed; try ssh -v";
+		keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey);
+		break;
+	case 1:
+		keymbox.msg = smprint("o0000");
+		retval = 0;
+		break;
+	}
+	nbsendul(keymbox.mchan, 1);
+	if (retval == 0)
+		genkeys(c, h, k);
+out:
+	mpfree(k);
+	free(ks);
+	free(sig);
+	free(pack2);
+	free(srvkey);
+	return retval;
+}
+
+static int
+dh_client141(Conn *c, Packet *)
+{
+	Packet *p;
+	mpint *e, *x;
+	int n;
+
+	/* Compute e: RFC4253 */
+	e = mpnew(2048);
+	x = mprand(256, genrandom, nil);
+	mpexp(two, x, p14, e);
+	p = new_packet(c);
+	add_byte(p, SSH_MSG_KEXDH_INIT);
+	add_mp(p, e);
+	n = finish_packet(p);
+	iowrite(c->dio, c->datafd, p->nlength, n);
+	free(p);
+	mpfree(e);
+	mpfree(x);
+	return 0;
+}
+
+static int
+dh_client142(Conn *, Packet *)
+{
+	return 0;
+}
+
+static void
+initsha1pkt(Packet *pack2, mpint *k, uchar *h)
+{
+	init_packet(pack2);
+	add_mp(pack2, k);
+	add_packet(pack2, h, SHA1dlen);
+}
+
+static void
+genkeys(Conn *c, uchar h[], mpint *k)
+{
+	Packet *pack2;
+	char buf[82], *bp, *be;			/* magic 82 */
+	int n;
+
+	pack2 = new_packet(c);
+	/* Compute 40 bytes (320 bits) of keys: each alg can use what it needs */
+
+	/* Client to server IV */
+	if (debug > 1) {
+		fprint(2, "k=%M\nh=", k);
+		for (n = 0; n < SHA1dlen; ++n)
+			fprint(2, "%02ux", h[n]);
+		fprint(2, "\nsessid=");
+		for (n = 0; n < SHA1dlen; ++n)
+			fprint(2, "%02ux", c->sessid[n]);
+		fprint(2, "\n");
+	}
+	initsha1pkt(pack2, k, h);
+	add_byte(pack2, 'A');
+	add_packet(pack2, c->sessid, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->nc2siv, nil);
+	initsha1pkt(pack2, k, h);
+	add_packet(pack2, c->nc2siv, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->nc2siv + SHA1dlen, nil);
+
+	/* Server to client IV */
+	initsha1pkt(pack2, k, h);
+	add_byte(pack2, 'B');
+	add_packet(pack2, c->sessid, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->ns2civ, nil);
+	initsha1pkt(pack2, k, h);
+	add_packet(pack2, c->ns2civ, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->ns2civ + SHA1dlen, nil);
+
+	/* Client to server encryption key */
+	initsha1pkt(pack2, k, h);
+	add_byte(pack2, 'C');
+	add_packet(pack2, c->sessid, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->nc2sek, nil);
+	initsha1pkt(pack2, k, h);
+	add_packet(pack2, c->nc2sek, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->nc2sek + SHA1dlen, nil);
+
+	/* Server to client encryption key */
+	initsha1pkt(pack2, k, h);
+	add_byte(pack2, 'D');
+	add_packet(pack2, c->sessid, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->ns2cek, nil);
+	initsha1pkt(pack2, k, h);
+	add_packet(pack2, c->ns2cek, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->ns2cek + SHA1dlen, nil);
+
+	/* Client to server integrity key */
+	initsha1pkt(pack2, k, h);
+	add_byte(pack2, 'E');
+	add_packet(pack2, c->sessid, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->nc2sik, nil);
+	initsha1pkt(pack2, k, h);
+	add_packet(pack2, c->nc2sik, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->nc2sik + SHA1dlen, nil);
+
+	/* Server to client integrity key */
+	initsha1pkt(pack2, k, h);
+	add_byte(pack2, 'F');
+	add_packet(pack2, c->sessid, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->ns2cik, nil);
+	initsha1pkt(pack2, k, h);
+	add_packet(pack2, c->ns2cik, SHA1dlen);
+	sha1(pack2->payload, pack2->rlength - 1, c->ns2cik + SHA1dlen, nil);
+
+	if (debug > 1) {
+		be = buf + sizeof buf;
+		fprint(2, "Client to server IV:\n");
+		for (n = 0, bp = buf; n < SHA1dlen*2; ++n)
+			bp = seprint(bp, be, "%02x", c->nc2siv[n]);
+		fprint(2, "%s\n", buf);
+
+		fprint(2, "Server to client IV:\n");
+		for (n = 0, bp = buf; n < SHA1dlen*2; ++n)
+			bp = seprint(bp, be, "%02x", c->ns2civ[n]);
+		fprint(2, "%s\n", buf);
+
+		fprint(2, "Client to server EK:\n");
+		for (n = 0, bp = buf; n < SHA1dlen*2; ++n)
+			bp = seprint(bp, be, "%02x", c->nc2sek[n]);
+		fprint(2, "%s\n", buf);
+
+		fprint(2, "Server to client EK:\n");
+		for (n = 0, bp = buf; n < SHA1dlen*2; ++n)
+			bp = seprint(bp, be, "%02x", c->ns2cek[n]);
+		fprint(2, "%s\n", buf);
+	}
+	free(pack2);
+}
+
+Kex dh1sha1 = {
+	"diffie-hellman-group1-sha1",
+	dh_server1,
+	dh_client11,
+	dh_client12
+};
+
+Kex dh14sha1 = {
+	"diffie-hellman-group14-sha1",
+	dh_server14,
+	dh_client141,
+	dh_client142
+};
+
+PKA rsa_pka = {
+	"ssh-rsa",
+	rsa_ks,
+	rsa_sign,
+	rsa_verify
+};
+
+PKA dss_pka = {
+	"ssh-dss",
+	dss_ks,
+	dss_sign,
+	dss_verify
+};

+ 559 - 0
sys/src/cmd/ssh2/dial.c

@@ -0,0 +1,559 @@
+/*
+ * dial - connect to a service (parallel version)
+ */
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+typedef struct Conn Conn;
+typedef struct Dest Dest;
+typedef struct DS DS;
+
+enum
+{
+	Maxstring	= 128,
+	Maxpath		= 256,
+
+	Maxcsreply	= 64*80,	/* this is probably overly generous */
+	/*
+	 * this should be a plausible slight overestimate for non-interactive
+	 * use even if it's ridiculously long for interactive use.
+	 */
+	Maxconnms	= 2*60*1000,	/* 2 minutes */
+};
+
+struct DS {
+	/* dist string */
+	char	buf[Maxstring];
+	char	*netdir;		/* e.g., /net.alt */
+	char	*proto;			/* e.g., tcp */
+	char	*rem;			/* e.g., host!service */
+
+	/* other args */
+	char	*local;
+	char	*dir;
+	int	*cfdp;
+};
+
+/*
+ * malloc these; they need to be writable by this proc & all children.
+ * the stack is private to each proc, and static allocation in the data
+ * segment would not permit concurrent dials within a multi-process program.
+ */
+struct Conn {
+	int	pid;
+	int	dead;
+
+	int	dfd;
+	int	cfd;
+	char	dir[NETPATHLEN];
+	char	err[ERRMAX];
+};
+struct Dest {
+	Conn	*conn;			/* allocated array */
+	Conn	*connend;
+	int	nkid;
+
+	long	oalarm;
+	int	naddrs;
+
+	QLock	winlck;
+	int	winner;			/* index into conn[] */
+
+	char	*nextaddr;
+	char	addrlist[Maxcsreply];
+};
+
+static int	call(char*, char*, DS*, Dest*, Conn*);
+static int	csdial(DS*);
+static void	_dial_string_parse(char*, DS*);
+
+
+/*
+ *  the dialstring is of the form '[/net/]proto!dest'
+ */
+static int
+dialimpl(char *dest, char *local, char *dir, int *cfdp)
+{
+	DS ds;
+	int rv;
+	char err[ERRMAX], alterr[ERRMAX];
+
+	ds.local = local;
+	ds.dir = dir;
+	ds.cfdp = cfdp;
+
+	_dial_string_parse(dest, &ds);
+	if(ds.netdir)
+		return csdial(&ds);
+
+	ds.netdir = "/net";
+	rv = csdial(&ds);
+	if(rv >= 0)
+		return rv;
+	err[0] = '\0';
+	errstr(err, sizeof err);
+	if(strstr(err, "refused") != 0){
+		werrstr("%s", err);
+		return rv;
+	}
+	ds.netdir = "/net.alt";
+	rv = csdial(&ds);
+	if(rv >= 0)
+		return rv;
+
+	alterr[0] = 0;
+	errstr(alterr, sizeof alterr);
+	if(strstr(alterr, "translate") || strstr(alterr, "does not exist"))
+		werrstr("%s", err);
+	else
+		werrstr("%s", alterr);
+	return rv;
+}
+
+/*
+ * the thread library can't cope with rfork(RFMEM|RFPROC),
+ * so it must override this with a private version of dial.
+ */
+int (*_dial)(char *, char *, char *, int *) = dialimpl;
+
+int
+dial(char *dest, char *local, char *dir, int *cfdp)
+{
+	return (*_dial)(dest, local, dir, cfdp);
+}
+
+static int
+connsalloc(Dest *dp, int addrs)
+{
+	Conn *conn;
+
+	free(dp->conn);
+	dp->connend = nil;
+	assert(addrs > 0);
+
+	dp->conn = mallocz(addrs * sizeof *dp->conn, 1);
+	if(dp->conn == nil)
+		return -1;
+	dp->connend = dp->conn + addrs;
+	for(conn = dp->conn; conn < dp->connend; conn++)
+		conn->cfd = conn->dfd = -1;
+	return 0;
+}
+
+static void
+freedest(Dest *dp)
+{
+	long oalarm;
+
+	if (dp == nil)
+		return;
+	oalarm = dp->oalarm;
+	free(dp->conn);
+	free(dp);
+	if (oalarm >= 0)
+		alarm(oalarm);
+}
+
+static void
+closeopenfd(int *fdp)
+{
+	if (*fdp >= 0) {
+		close(*fdp);
+		*fdp = -1;
+	}
+}
+
+static void
+notedeath(Dest *dp, char *exitsts)
+{
+	int i, n, pid;
+	char *fields[5];			/* pid + 3 times + error */
+	Conn *conn;
+
+	for (i = 0; i < nelem(fields); i++)
+		fields[i] = "";
+	n = tokenize(exitsts, fields, nelem(fields));
+	if (n < 4)
+		return;
+	pid = atoi(fields[0]);
+	if (pid <= 0)
+		return;
+	for (conn = dp->conn; conn < dp->connend; conn++)
+		if (conn->pid == pid && !conn->dead) {  /* it's one we know? */
+			if (conn - dp->conn != dp->winner) {
+				closeopenfd(&conn->dfd);
+				closeopenfd(&conn->cfd);
+			}
+			strncpy(conn->err, fields[4], sizeof conn->err);
+			conn->dead = 1;
+			return;
+		}
+	/* not a proc that we forked */
+}
+
+static int
+outstandingprocs(Dest *dp)
+{
+	Conn *conn;
+
+	for (conn = dp->conn; conn < dp->connend; conn++)
+		if (!conn->dead)
+			return 1;
+	return 0;
+}
+
+static int
+reap(Dest *dp)
+{
+	char exitsts[2*ERRMAX];
+
+	if (outstandingprocs(dp) && await(exitsts, sizeof exitsts) >= 0) {
+		notedeath(dp, exitsts);
+		return 0;
+	}
+	return -1;
+}
+
+static int
+fillinds(DS *ds, Dest *dp)
+{
+	Conn *conn;
+
+	if (dp->winner < 0)
+		return -1;
+	conn = &dp->conn[dp->winner];
+	if (ds->cfdp)
+		*ds->cfdp = conn->cfd;
+	if (ds->dir)
+		strncpy(ds->dir, conn->dir, NETPATHLEN);
+	return conn->dfd;
+}
+
+static int
+connectwait(Dest *dp, char *besterr)
+{
+	Conn *conn;
+
+	/* wait for a winner or all attempts to time out */
+	while (dp->winner < 0 && reap(dp) >= 0)
+		;
+
+	/* kill all of our still-live kids & reap them */
+	for (conn = dp->conn; conn < dp->connend; conn++)
+		if (!conn->dead)
+			postnote(PNPROC, conn->pid, "alarm");
+	while (reap(dp) >= 0)
+		;
+
+	/* rummage about and report some error string */
+	for (conn = dp->conn; conn < dp->connend; conn++)
+		if (conn - dp->conn != dp->winner && conn->dead &&
+		    conn->err[0]) {
+			strncpy(besterr, conn->err, ERRMAX);
+			break;
+		}
+	return dp->winner;
+}
+
+static int
+parsecs(Dest *dp, char **clonep, char **destp)
+{
+	char *dest, *p;
+
+	dest = strchr(dp->nextaddr, ' ');
+	if(dest == nil)
+		return -1;
+	*dest++ = '\0';
+	p = strchr(dest, '\n');
+	if(p == nil)
+		return -1;
+	*p++ = '\0';
+	*clonep = dp->nextaddr;
+	*destp = dest;
+	dp->nextaddr = p;		/* advance to next line */
+	return 0;
+}
+
+static void
+pickuperr(char *besterr, char *err)
+{
+	err[0] = '\0';
+	errstr(err, ERRMAX);
+	if(strstr(err, "does not exist") == 0)
+		strcpy(besterr, err);
+}
+
+static void
+catcher(void *, char *s)
+{
+	if (strstr(s, "alarm") != nil)
+		noted(NCONT);
+	else
+		noted(NDFLT);
+}
+
+/*
+ * try all addresses in parallel and take the first one that answers;
+ * this helps when systems have ip v4 and v6 addresses but are
+ * only reachable from here on one (or some) of them.
+ */
+static int
+dialmulti(DS *ds, Dest *dp)
+{
+	int rv, kid, kidme;
+	char *clone, *dest;
+	char err[ERRMAX], besterr[ERRMAX];
+
+	dp->winner = -1;
+	dp->nkid = 0;
+	while(dp->winner < 0 && *dp->nextaddr != '\0' &&
+	    parsecs(dp, &clone, &dest) >= 0) {
+		kidme = dp->nkid++;		/* make private copy on stack */
+		kid = rfork(RFPROC|RFMEM);	/* spin off a call attempt */
+		if (kid < 0)
+			--dp->nkid;
+		else if (kid == 0) {
+			/* only in kid, to avoid atnotify callbacks in parent */
+			notify(catcher);
+
+			*besterr = '\0';
+			rv = call(clone, dest, ds, dp, &dp->conn[kidme]);
+			if(rv < 0)
+				pickuperr(besterr, err);
+			_exits(besterr);	/* avoid atexit callbacks */
+		}
+	}
+	rv = connectwait(dp, besterr);
+	if(rv < 0 && *besterr)
+		werrstr("%s", besterr);
+	else
+		werrstr("%s", err);
+	return rv;
+}
+
+static int
+csdial(DS *ds)
+{
+	int n, fd, rv, addrs, bleft;
+	char c;
+	char *addrp, *clone2, *dest;
+	char buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX];
+	Dest *dp;
+
+	dp = mallocz(sizeof *dp, 1);
+	if(dp == nil)
+		return -1;
+	dp->winner = -1;
+	dp->oalarm = alarm(0);
+	if (connsalloc(dp, 1) < 0) {		/* room for a single conn. */
+		freedest(dp);
+		return -1;
+	}
+
+	/*
+	 *  open connection server
+	 */
+	snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
+	fd = open(buf, ORDWR);
+	if(fd < 0){
+		/* no connection server, don't translate */
+		snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
+		rv = call(clone, ds->rem, ds, dp, &dp->conn[0]);
+		fillinds(ds, dp);
+		freedest(dp);
+		return rv;
+	}
+
+	/*
+	 *  ask connection server to translate
+	 */
+	snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem);
+	if(write(fd, buf, strlen(buf)) < 0){
+		close(fd);
+		freedest(dp);
+		return -1;
+	}
+
+	/*
+	 *  read all addresses from the connection server.
+	 */
+	seek(fd, 0, 0);
+	addrs = 0;
+	addrp = dp->nextaddr = dp->addrlist;
+	bleft = sizeof dp->addrlist - 2;	/* 2 is room for \n\0 */
+	while(bleft > 0 && (n = read(fd, addrp, bleft)) > 0) {
+		if (addrp[n-1] != '\n')
+			addrp[n++] = '\n';
+		addrs++;
+		addrp += n;
+		bleft -= n;
+	}
+	/*
+	 * if we haven't read all of cs's output, assume the last line might
+	 * have been truncated and ignore it.  we really don't expect this
+	 * to happen.
+	 */
+	if (addrs > 0 && bleft <= 0 && read(fd, &c, 1) == 1)
+		addrs--;
+	close(fd);
+
+	*besterr = 0;
+	rv = -1;				/* pessimistic default */
+	dp->naddrs = addrs;
+	if (addrs == 0)
+		werrstr("no address to dial");
+	else if (addrs == 1) {
+		/* common case: dial one address without forking */
+		if (parsecs(dp, &clone2, &dest) >= 0 &&
+		    (rv = call(clone2, dest, ds, dp, &dp->conn[0])) < 0) {
+			pickuperr(besterr, err);
+			werrstr("%s", besterr);
+		}
+	} else if (connsalloc(dp, addrs) >= 0)
+		rv = dialmulti(ds, dp);
+
+	/* fill in results */
+	if (rv >= 0 && dp->winner >= 0)
+		rv = fillinds(ds, dp);
+
+	freedest(dp);
+	return rv;
+}
+
+static int
+call(char *clone, char *dest, DS *ds, Dest *dp, Conn *conn)
+{
+	int fd, cfd, n, calleralarm, oalarm;
+	char cname[Maxpath], name[Maxpath], data[Maxpath], *p;
+
+	/* because cs is in a different name space, replace the mount point */
+	if(*clone == '/'){
+		p = strchr(clone+1, '/');
+		if(p == nil)
+			p = clone;
+		else 
+			p++;
+	} else
+		p = clone;
+	snprint(cname, sizeof cname, "%s/%s", ds->netdir, p);
+
+	conn->pid = getpid();
+	conn->cfd = cfd = open(cname, ORDWR);
+	if(cfd < 0)
+		return -1;
+
+	/* get directory name */
+	n = read(cfd, name, sizeof(name)-1);
+	if(n < 0){
+		closeopenfd(&conn->cfd);
+		return -1;
+	}
+	name[n] = 0;
+	for(p = name; *p == ' '; p++)
+		;
+	snprint(name, sizeof(name), "%ld", strtoul(p, 0, 0));
+	p = strrchr(cname, '/');
+	*p = 0;
+	if(ds->dir)
+		snprint(conn->dir, NETPATHLEN, "%s/%s", cname, name);
+	snprint(data, sizeof(data), "%s/%s/data", cname, name);
+
+	/* should be no alarm pending now; re-instate caller's alarm, if any */
+	calleralarm = dp->oalarm > 0;
+	if (calleralarm)
+		alarm(dp->oalarm);
+	else if (dp->naddrs > 1)	/* in a sub-process? */
+		alarm(Maxconnms);
+
+	/* connect */
+	if(ds->local)
+		snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
+	else
+		snprint(name, sizeof(name), "connect %s", dest);
+	if(write(cfd, name, strlen(name)) < 0){
+		closeopenfd(&conn->cfd);
+		return -1;
+	}
+
+	oalarm = alarm(0);	/* don't let alarm interrupt critical section */
+	if (calleralarm)
+		dp->oalarm = oalarm;	/* time has passed, so update user's */
+
+	/* open data connection */
+	conn->dfd = fd = open(data, ORDWR);
+	if(fd < 0){
+		closeopenfd(&conn->cfd);
+		alarm(dp->oalarm);
+		return -1;
+	}
+	if(ds->cfdp == nil)
+		closeopenfd(&conn->cfd);
+
+	n = conn - dp->conn;
+	if (dp->winner < 0) {
+		qlock(&dp->winlck);
+		if (dp->winner < 0 && conn < dp->connend)
+			dp->winner = n;
+		qunlock(&dp->winlck);
+	}
+	alarm(calleralarm? dp->oalarm: 0);
+	return fd;
+}
+
+/*
+ * assume p points at first '!' in dial string.  st is start of dial string.
+ * there could be subdirs of the conn dirs (e.g., ssh/0) that must count as
+ * part of the proto string, so skip numeric components.
+ * returns pointer to delimiter after right-most non-numeric component.
+ */
+static char *
+backoverchans(char *st, char *p)
+{
+	char *sl;
+
+	for (sl = p; --p >= st && isascii(*p) && isdigit(*p); sl = p) {
+		while (--p >= st && isascii(*p) && isdigit(*p))
+			;
+		if (p < st || *p != '/')
+			break;			/* "net.alt2" or ran off start */
+		while (p > st && p[-1] == '/')	/* skip runs of slashes */
+			p--;
+	}
+	return sl;
+}
+
+/*
+ *  parse a dial string
+ */
+static void
+_dial_string_parse(char *str, DS *ds)
+{
+	char *p, *p2;
+
+	strncpy(ds->buf, str, Maxstring);
+	ds->buf[Maxstring-1] = 0;
+
+	p = strchr(ds->buf, '!');
+	if(p == 0) {
+		ds->netdir = 0;
+		ds->proto = "net";
+		ds->rem = ds->buf;
+	} else {
+		if(*ds->buf != '/' && *ds->buf != '#'){
+			ds->netdir = 0;
+			ds->proto = ds->buf;
+		} else {
+			p2 = backoverchans(ds->buf, p);
+
+			/* back over last component of netdir (proto) */
+			while (--p2 > ds->buf && *p2 != '/')
+				;
+			*p2++ = 0;
+			ds->netdir = ds->buf;
+			ds->proto = p2;
+		}
+		*p = 0;
+		ds->rem = p + 1;
+	}
+}

+ 553 - 0
sys/src/cmd/ssh2/dial.thread.c

@@ -0,0 +1,553 @@
+/*
+ * dial - connect to a service (threaded parallel version)
+ */
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+typedef struct Conn Conn;
+typedef struct Dest Dest;
+typedef struct DS DS;
+typedef struct Kidargs Kidargs;
+typedef struct Restup Restup;
+
+enum
+{
+	Noblock,
+	Block,
+
+	Defstksize	= 8192,
+
+	Maxstring	= 128,
+	Maxpath		= 256,
+
+	Maxcsreply	= 64*80,	/* this is probably overly generous */
+	/*
+	 * this should be a plausible slight overestimate for non-interactive
+	 * use even if it's ridiculously long for interactive use.
+	 */
+	Maxconnms	= 2*60*1000,	/* 2 minutes */
+};
+
+struct DS {
+	/* dial string */
+	char	buf[Maxstring];
+	char	*netdir;
+	char	*proto;
+	char	*rem;
+
+	/* other args */
+	char	*local;
+	char	*dir;
+	int	*cfdp;
+};
+
+struct Conn {
+	int	cfd;
+	char	dir[NETPATHLEN];
+};
+struct Dest {
+	DS	*ds;
+
+	Channel *reschan;		/* all callprocs send results on this */
+	int	nkid;
+	int	kidthrids[64];		/* one per addr; ought to be enough */
+
+	int	windfd;
+	char	err[ERRMAX];
+
+	long	oalarm;
+
+	int	naddrs;
+	char	*nextaddr;
+	char	addrlist[Maxcsreply];
+};
+
+struct Kidargs {			/* arguments to callproc */
+	Dest	*dp;
+	int	thridsme;
+	char	*clone;
+	char	*dest;
+};
+
+struct Restup {				/* result tuple from callproc */
+	int	dfd;
+	int	cfd;
+	char	*err;
+	char	*conndir;
+};
+
+static int	call(char*, char*, Dest*, Conn*);
+static int	call1(char*, char*, Dest*, Conn*);
+static int	csdial(DS*);
+static void	_dial_string_parse(char*, DS*);
+
+/*
+ *  the dialstring is of the form '[/net/]proto!dest'
+ */
+static int
+dialimpl(char *dest, char *local, char *dir, int *cfdp)
+{
+	DS ds;
+	int rv;
+	char err[ERRMAX], alterr[ERRMAX];
+
+	ds.local = local;
+	ds.dir = dir;
+	ds.cfdp = cfdp;
+
+	_dial_string_parse(dest, &ds);
+	if(ds.netdir)
+		return csdial(&ds);
+
+	ds.netdir = "/net";
+	rv = csdial(&ds);
+	if(rv >= 0)
+		return rv;
+	err[0] = '\0';
+	errstr(err, sizeof err);
+	if(strstr(err, "refused") != 0){
+		werrstr("%s", err);
+		return rv;
+	}
+	ds.netdir = "/net.alt";
+	rv = csdial(&ds);
+	if(rv >= 0)
+		return rv;
+
+	alterr[0] = 0;
+	errstr(alterr, sizeof alterr);
+	if(strstr(alterr, "translate") || strstr(alterr, "does not exist"))
+		werrstr("%s", err);
+	else
+		werrstr("%s", alterr);
+	return rv;
+}
+
+/*
+ * the thread library can't cope with rfork(RFMEM|RFPROC),
+ * so it must override _dial with this version of dial.
+ */
+int (*_dial)(char *, char *, char *, int *) = dialimpl;
+
+int
+dial(char *dest, char *local, char *dir, int *cfdp)
+{
+	return (*_dial)(dest, local, dir, cfdp);
+}
+
+static void
+freedest(Dest *dp)
+{
+	if (dp) {
+		if (dp->oalarm >= 0)
+			alarm(dp->oalarm);
+		free(dp);
+	}
+}
+
+static void
+closeopenfd(int *fdp)
+{
+	if (*fdp >= 0) {
+		close(*fdp);
+		*fdp = -1;
+	}
+}
+
+static int
+parsecs(Dest *dp, char **clonep, char **destp)
+{
+	char *dest, *p;
+
+	dest = strchr(dp->nextaddr, ' ');
+	if(dest == nil)
+		return -1;
+	*dest++ = '\0';
+	p = strchr(dest, '\n');
+	if(p == nil)
+		return -1;
+	*p++ = '\0';
+	*clonep = dp->nextaddr;
+	*destp = dest;
+	dp->nextaddr = p;		/* advance to next line */
+	return 0;
+}
+
+static void
+pickuperr(char *besterr)
+{
+	char err[ERRMAX];
+
+	err[0] = '\0';
+	errstr(err, ERRMAX);
+	if(strstr(err, "does not exist") == 0)
+		strcpy(besterr, err);
+}
+
+static int
+catcher(void *, char *s)
+{
+	return strstr(s, "alarm") != nil;
+}
+
+static void
+callproc(void *p)
+{
+	int dfd;
+	char besterr[ERRMAX];
+	Conn lconn;
+	Conn *conn;
+	Kidargs *args;
+	Restup *tup;
+
+	threadnotify(catcher, 1);	/* avoid atnotify callbacks in parent */
+
+	conn = &lconn;
+	memset(conn, 0, sizeof *conn);
+	*besterr = '\0';
+	args = (Kidargs *)p;
+	dfd = call(args->clone, args->dest, args->dp, conn);
+	if(dfd < 0)
+		pickuperr(besterr);
+
+	tup = (Restup *)emalloc9p(sizeof *tup);
+	*tup = (Restup){dfd, conn->cfd, nil, nil};
+	if (dfd >= 0)
+		tup->conndir = strdup(conn->dir);
+	else
+		tup->err = strdup(besterr);
+	sendp(args->dp->reschan, tup);
+
+	args->dp->kidthrids[args->thridsme] = -1;
+	free(args);
+	threadexits(besterr);		/* better be no atexit callbacks */
+}
+
+/* interrupt all of our still-live kids */
+static void
+intrcallprocs(Dest *dp)
+{
+	int i;
+
+	for (i = 0; i < nelem(dp->kidthrids); i++)
+		if (dp->kidthrids[i] >= 0)
+			threadint(dp->kidthrids[i]);
+}
+
+static int
+recvresults(Dest *dp, int block)
+{
+	DS *ds;
+	Restup *tup;
+
+	for (; dp->nkid > 0; dp->nkid--) {
+		if (block)
+			tup = recvp(dp->reschan);
+		else
+			tup = nbrecvp(dp->reschan);
+		if (tup == nil)
+			break;
+		if (tup->dfd >= 0)		/* connected? */
+			if (dp->windfd < 0) {	/* first connection? */
+				ds = dp->ds;
+				dp->windfd = tup->dfd;
+				if (ds->cfdp)
+					*ds->cfdp = tup->cfd;
+				if (ds->dir)
+					strncpy(ds->dir, tup->conndir,
+						NETPATHLEN);
+				intrcallprocs(dp);
+			} else {
+				close(tup->dfd);
+				close(tup->cfd);
+			}
+		else if (dp->err[0] == '\0' && tup->err)
+			strncpy(dp->err, tup->err, ERRMAX);
+		free(tup->conndir);
+		free(tup->err);
+		free(tup);
+	}
+	return dp->windfd;
+}
+
+/*
+ * try all addresses in parallel and take the first one that answers;
+ * this helps when systems have ip v4 and v6 addresses but are
+ * only reachable from here on one (or some) of them.
+ */
+static int
+dialmulti(Dest *dp)
+{
+	int kidme;
+	char *clone, *dest;
+	Kidargs *argp;
+
+	dp->reschan = chancreate(sizeof(void *), 0);
+	dp->err[0] = '\0';
+	dp->nkid = 0;
+	dp->windfd = -1;
+	/* if too many addresses for dp->kidthrids, ignore the last few */
+	while(dp->windfd < 0 && dp->nkid < nelem(dp->kidthrids) &&
+	    *dp->nextaddr != '\0' && parsecs(dp, &clone, &dest) >= 0) {
+		kidme = dp->nkid++;
+
+		argp = (Kidargs *)emalloc9p(sizeof *argp);
+		*argp = (Kidargs){dp, kidme, clone, dest};
+
+		dp->kidthrids[kidme] = proccreate(callproc, argp, Defstksize);
+		if (dp->kidthrids[kidme] < 0)
+			--dp->nkid;
+	}
+
+	recvresults(dp, Block);
+	assert(dp->nkid == 0);
+
+	chanclose(dp->reschan);
+	chanfree(dp->reschan);
+	if(dp->windfd < 0 && dp->err[0])
+		werrstr("%s", dp->err);
+	return dp->windfd;
+}
+
+/* call a single address and pass back cfd & conn dir after */
+static int
+call1(char *clone, char *rem, Dest *dp, Conn *conn)
+{
+	int dfd;
+	DS *ds;
+
+	ds = dp->ds;
+	dfd = call(clone, rem, dp, conn);
+	if (dfd < 0)
+		return dfd;
+
+	if (ds->cfdp)
+		*ds->cfdp = conn->cfd;
+	if (ds->dir)
+		strncpy(ds->dir, conn->dir, NETPATHLEN);
+	return dfd;
+}
+
+static int
+csdial(DS *ds)
+{
+	int n, fd, dfd, addrs, bleft;
+	char c;
+	char *addrp, *clone2, *dest;
+	char buf[Maxstring], clone[Maxpath], besterr[ERRMAX];
+	Conn lconn;
+	Conn *conn;
+	Dest *dp;
+
+	dp = mallocz(sizeof *dp, 1);
+	if(dp == nil)
+		return -1;
+	conn = &lconn;
+	memset(conn, 0, sizeof *conn);
+	dp->ds = ds;
+	if (ds->cfdp)
+		*ds->cfdp = -1;
+	if (ds->dir)
+		ds->dir[0] = '\0';
+	dp->oalarm = alarm(0);
+
+	/*
+	 *  open connection server
+	 */
+	snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
+	fd = open(buf, ORDWR);
+	if(fd < 0){
+		/* no connection server, don't translate */
+		snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
+		dfd = call1(clone, ds->rem, dp, conn);
+		freedest(dp);
+		return dfd;
+	}
+
+	/*
+	 *  ask connection server to translate
+	 */
+	snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem);
+	if(write(fd, buf, strlen(buf)) < 0){
+		close(fd);
+		freedest(dp);
+		return -1;
+	}
+
+	/*
+	 *  read all addresses from the connection server.
+	 */
+	seek(fd, 0, 0);
+	addrs = 0;
+	addrp = dp->nextaddr = dp->addrlist;
+	bleft = sizeof dp->addrlist - 2;	/* 2 is room for \n\0 */
+	while(bleft > 0 && (n = read(fd, addrp, bleft)) > 0) {
+		if (addrp[n-1] != '\n')
+			addrp[n++] = '\n';
+		addrs++;
+		addrp += n;
+		bleft -= n;
+	}
+	/*
+	 * if we haven't read all of cs's output, assume the last line might
+	 * have been truncated and ignore it.  we really don't expect this
+	 * to happen.
+	 */
+	if (addrs > 0 && bleft <= 0 && read(fd, &c, 1) == 1)
+		addrs--;
+	close(fd);
+
+	*besterr = 0;
+	dfd = -1;				/* pessimistic default */
+	dp->naddrs = addrs;
+	if (addrs == 0)
+		werrstr("no address to dial");
+	else if (addrs == 1) {
+		/* common case: dial one address without forking */
+		if (parsecs(dp, &clone2, &dest) >= 0 &&
+		    (dfd = call1(clone2, dest, dp, conn)) < 0) {
+			pickuperr(besterr);
+			werrstr("%s", besterr);
+		}
+	} else
+		dfd = dialmulti(dp);
+
+	freedest(dp);
+	return dfd;
+}
+
+/* returns dfd, stores cfd through cfdp */
+static int
+call(char *clone, char *dest, Dest *dp, Conn *conn)
+{
+	int fd, cfd, n, calleralarm, oalarm;
+	char cname[Maxpath], name[Maxpath], data[Maxpath], *p;
+	DS *ds;
+
+	/* because cs is in a different name space, replace the mount point */
+	if(*clone == '/'){
+		p = strchr(clone+1, '/');
+		if(p == nil)
+			p = clone;
+		else 
+			p++;
+	} else
+		p = clone;
+	ds = dp->ds;
+	snprint(cname, sizeof cname, "%s/%s", ds->netdir, p);
+
+	conn->cfd = cfd = open(cname, ORDWR);
+	if(cfd < 0)
+		return -1;
+
+	/* get directory name */
+	n = read(cfd, name, sizeof(name)-1);
+	if(n < 0){
+		closeopenfd(&conn->cfd);
+		return -1;
+	}
+	name[n] = 0;
+	for(p = name; *p == ' '; p++)
+		;
+	snprint(name, sizeof(name), "%ld", strtoul(p, 0, 0));
+	p = strrchr(cname, '/');
+	*p = 0;
+	if(ds->dir)
+		snprint(conn->dir, NETPATHLEN, "%s/%s", cname, name);
+	snprint(data, sizeof(data), "%s/%s/data", cname, name);
+
+	/* should be no alarm pending now; re-instate caller's alarm, if any */
+	calleralarm = dp->oalarm > 0;
+	if (calleralarm)
+		alarm(dp->oalarm);
+	else if (dp->naddrs > 1)	/* in a sub-process? */
+		alarm(Maxconnms);
+
+	/* connect */
+	if(ds->local)
+		snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
+	else
+		snprint(name, sizeof(name), "connect %s", dest);
+	if(write(cfd, name, strlen(name)) < 0){
+		closeopenfd(&conn->cfd);
+		return -1;
+	}
+
+	oalarm = alarm(0);	/* don't let alarm interrupt critical section */
+	if (calleralarm)
+		dp->oalarm = oalarm;	/* time has passed, so update user's */
+
+	/* open data connection */
+	fd = open(data, ORDWR);
+	if(fd < 0){
+		closeopenfd(&conn->cfd);
+		alarm(dp->oalarm);
+		return -1;
+	}
+	if(ds->cfdp == nil)
+		closeopenfd(&conn->cfd);
+
+	alarm(calleralarm? dp->oalarm: 0);
+	return fd;
+}
+
+/*
+ * assume p points at first '!' in dial string.  st is start of dial string.
+ * there could be subdirs of the conn dirs (e.g., ssh/0) that must count as
+ * part of the proto string, so skip numeric components.
+ * returns pointer to delimiter after right-most non-numeric component.
+ */
+static char *
+backoverchans(char *st, char *p)
+{
+	char *sl;
+
+	for (sl = p; --p >= st && isascii(*p) && isdigit(*p); sl = p) {
+		while (--p >= st && isascii(*p) && isdigit(*p))
+			;
+		if (p < st || *p != '/')
+			break;			/* "net.alt2" or ran off start */
+		while (p > st && p[-1] == '/')	/* skip runs of slashes */
+			p--;
+	}
+	return sl;
+}
+
+/*
+ *  parse a dial string
+ */
+static void
+_dial_string_parse(char *str, DS *ds)
+{
+	char *p, *p2;
+
+	strncpy(ds->buf, str, Maxstring);
+	ds->buf[Maxstring-1] = 0;
+
+	p = strchr(ds->buf, '!');
+	if(p == 0) {
+		ds->netdir = 0;
+		ds->proto = "net";
+		ds->rem = ds->buf;
+	} else {
+		if(*ds->buf != '/' && *ds->buf != '#'){
+			ds->netdir = 0;
+			ds->proto = ds->buf;
+		} else {
+			p2 = backoverchans(ds->buf, p);
+
+			/* back over last component of netdir (proto) */
+			while (--p2 > ds->buf && *p2 != '/')
+				;
+			*p2++ = 0;
+			ds->netdir = ds->buf;
+			ds->proto = p2;
+		}
+		*p = 0;
+		ds->rem = p + 1;
+	}
+}

+ 3509 - 0
sys/src/cmd/ssh2/dup.out

@@ -0,0 +1,3509 @@
+#1. 9 ncsl lines
+	9,netssh.c:561,570
+	9,netssh.c:853,861
+	1 parameters
+ 	1. dataq,	reqq
+
+#2. 44 ncsl lines
+	6,dial.c:500,559
+	7,dial.thread.c:494,553
+	0 parameters
+
+#3. 3 ncsl lines
+	7,dial.thread.c:274,282
+	7,dial.thread.c:313,317
+	0 parameters
+
+#4. 33 ncsl lines
+	6,dial.c:442,481
+	7,dial.thread.c:443,482
+	0 parameters
+
+#5. 16 ncsl lines
+	9,netssh.c:2595,2611
+	9,netssh.c:2621,2637
+	6 parameters
+ 	1. "received KEX algs: %s",	"received C2S crypto algs: %s"
+ 	2. kexes,	cryptos
+ 	3. foundk,	foundc1
+ 	4. "kex algs not in kexes",	"c2s crypto algs not in cryptos"
+ 	5. kexalg,	ncscrypt
+ 	6. "received host key algs: %s",	"received S2C crypto algs: %s"
+
+#6. 16 ncsl lines
+	9,netssh.c:2595,2611
+	9,netssh.c:2634,2650
+	6 parameters
+ 	1. "received KEX algs: %s",	"received S2C crypto algs: %s"
+ 	2. kexes,	cryptos
+ 	3. foundk,	foundc2
+ 	4. "kex algs not in kexes",	"s2c crypto algs not in cryptos"
+ 	5. kexalg,	nsccrypt
+ 	6. "received host key algs: %s",	"received C2S MAC algs: %s"
+
+#7. 12 ncsl lines
+	9,netssh.c:2693,2705
+	9,netssh.c:2725,2737
+	3 parameters
+ 	1. kexes,	cryptos
+ 	2. foundk,	foundc2
+ 	3. kexalg,	nsccrypt
+
+#8. 12 ncsl lines
+	9,netssh.c:2693,2705
+	9,netssh.c:2715,2726
+	3 parameters
+ 	1. kexes,	cryptos
+ 	2. foundk,	foundc1
+ 	3. kexalg,	ncscrypt
+
+#9. 4 ncsl lines
+	9,netssh.c:2693,2696
+	9,netssh.c:2747,2750
+	1 parameters
+ 	1. kexes,	macnames
+
+#10. 4 ncsl lines
+	9,netssh.c:2693,2696
+	9,netssh.c:2736,2739
+	1 parameters
+ 	1. kexes,	macnames
+
+#11. 3 ncsl lines
+	9,netssh.c:2673,2675
+	9,netssh.c:2674,2676
+	(overlap; region has 3 nonoverlapping matching 1-ncsl-line segments)
+	0 parameters
+
+#12. 3 ncsl lines
+	9,netssh.c:2674,2676
+	9,netssh.c:2758,2760
+	0 parameters
+
+#13. 3 ncsl lines
+	9,netssh.c:2673,2675
+	9,netssh.c:2759,2761
+	0 parameters
+
+#14. 3 ncsl lines
+	9,netssh.c:2758,2760
+	9,netssh.c:2759,2761
+	(overlap; region has 3 nonoverlapping matching 1-ncsl-line segments)
+	0 parameters
+
+#15. 9 ncsl lines
+	9,netssh.c:1231,1239
+	9,netssh.c:1441,1449
+	2 parameters
+ 	1. Closing,	Closed
+ 	2. Closed,	Closing
+
+#16. 4 ncsl lines
+	9,netssh.c:1310,1317
+	9,netssh.c:1339,1342
+	1 parameters
+ 	1. "shell",	"exec"
+
+#17. 5 ncsl lines
+	6,dial.c:281,285
+	7,dial.thread.c:185,189
+	0 parameters
+
+#18. 3 ncsl lines
+	12,ssh.c:245,247
+	12,ssh.c:287,289
+	0 parameters
+
+#19. 8 ncsl lines
+	9,netssh.c:1582,1589
+	9,netssh.c:3160,3167
+	2 parameters
+ 	1. e,	x
+ 	2. x,	e
+
+#20. 4 ncsl lines
+	9,netssh.c:1586,1589
+	9,netssh.c:3160,3163
+	0 parameters
+
+#21. 4 ncsl lines
+	9,netssh.c:1582,1585
+	9,netssh.c:1586,1589
+	1 parameters
+ 	1. e,	x
+
+#22. 4 ncsl lines
+	9,netssh.c:1003,1006
+	9,netssh.c:1012,1015
+	1 parameters
+ 	1. authkey,	password
+
+#23. 4 ncsl lines
+	9,netssh.c:3160,3163
+	9,netssh.c:3164,3167
+	1 parameters
+ 	1. x,	e
+
+#24. 4 ncsl lines
+	9,netssh.c:1582,1585
+	9,netssh.c:3164,3167
+	0 parameters
+
+#25. 9 ncsl lines
+	9,netssh.c:159,168
+	11,rsa2ssh2.c:15,24
+	2 parameters
+ 	1. "usage: %s [-dkv] [-m mntpt] [-s srvpt]\n",	"usage: %s [file]\n"
+ 	2. threadmain,	main
+
+#26. 3 ncsl lines
+	9,netssh.c:159,161
+	12,ssh.c:24,26
+	0 parameters
+
+#27. 3 ncsl lines
+	9,netssh.c:159,161
+	13,sshsession.c:26,28
+	0 parameters
+
+#28. 6 ncsl lines
+	9,netssh.c:3185,3190
+	9,netssh.c:3191,3196
+	1 parameters
+ 	1. dataq,	reqq
+
+#29. 3 ncsl lines
+	9,netssh.c:1865,1867
+	9,netssh.c:1897,1899
+	0 parameters
+
+#30. 4 ncsl lines
+	5,dh.c:259,263
+	5,dh.c:320,324
+	1 parameters
+ 	1. nbit,	1024
+
+#31. 6 ncsl lines
+	9,netssh.c:2142,2147
+	9,netssh.c:2185,2190
+	2 parameters
+ 	1. SSH_DISCONNECT_KEY_EXCHANGE_FAILED,	SSH_DISCONNECT_SERVICE_NOT_AVAILABLE
+ 	2. "Key exchange failure",	"Unknown service type"
+
+#32. 3 ncsl lines
+	9,netssh.c:1324,1326
+	9,netssh.c:1325,1327
+	(overlap; region has 3 nonoverlapping matching 1-ncsl-line segments)
+	0 parameters
+
+#33. 3 ncsl lines
+	6,dial.c:331,333
+	7,dial.thread.c:113,115
+	0 parameters
+
+#34. 3 ncsl lines
+	6,dial.c:97,99
+	6,dial.c:331,333
+	0 parameters
+
+#35. 4 ncsl lines
+	9,netssh.c:132,136
+	9,netssh.c:151,155
+	1 parameters
+ 	1. "ssh",	"sshdebug"
+
+#36. 3 ncsl lines
+	8,esmprint.c:10,12
+	9,netssh.c:151,153
+	0 parameters
+
+#37. 3 ncsl lines
+	9,netssh.c:596,599
+	9,netssh.c:1524,1527
+	0 parameters
+
+#38. 4 ncsl lines
+	9,netssh.c:687,690
+	9,netssh.c:718,721
+	0 parameters
+
+#39. 4 ncsl lines
+	9,netssh.c:379,382
+	9,netssh.c:723,726
+	0 parameters
+
+#40. 4 ncsl lines
+	9,netssh.c:379,382
+	9,netssh.c:970,973
+	0 parameters
+
+#41. 4 ncsl lines
+	9,netssh.c:379,382
+	9,netssh.c:960,963
+	1 parameters
+ 	1. nil,	"deferredinit failed"
+
+#42. 4 ncsl lines
+	9,netssh.c:379,382
+	9,netssh.c:665,668
+	1 parameters
+ 	1. nil,	"deferredinit failed"
+
+#43. 3 ncsl lines
+	9,netssh.c:379,381
+	9,netssh.c:836,838
+	0 parameters
+
+#44. 3 ncsl lines
+	9,netssh.c:379,381
+	9,netssh.c:954,956
+	0 parameters
+
+#45. 3 ncsl lines
+	9,netssh.c:379,381
+	9,netssh.c:650,652
+	0 parameters
+
+#46. 3 ncsl lines
+	9,netssh.c:379,381
+	9,netssh.c:628,630
+	0 parameters
+
+#47. 3 ncsl lines
+	9,netssh.c:379,381
+	9,netssh.c:658,660
+	0 parameters
+
+#48. 3 ncsl lines
+	9,netssh.c:379,381
+	9,netssh.c:671,673
+	0 parameters
+
+#49. 3 ncsl lines
+	9,netssh.c:379,381
+	9,netssh.c:869,871
+	0 parameters
+
+#50. 3 ncsl lines
+	9,netssh.c:550,552
+	9,netssh.c:918,920
+	0 parameters
+
+#51. 3 ncsl lines
+	9,netssh.c:550,552
+	9,netssh.c:900,902
+	0 parameters
+
+#52. 5 ncsl lines
+	5,dh.c:280,284
+	5,dh.c:418,422
+	0 parameters
+
+#53. 4 ncsl lines
+	5,dh.c:154,157
+	5,dh.c:187,190
+	1 parameters
+ 	1. "no proto=dsa key in factotum",	"no key (y) found"
+
+#54. 4 ncsl lines
+	5,dh.c:154,157
+	5,dh.c:180,183
+	1 parameters
+ 	1. "no proto=dsa key in factotum",	"no key (g) found"
+
+#55. 4 ncsl lines
+	5,dh.c:154,157
+	5,dh.c:173,176
+	1 parameters
+ 	1. "no proto=dsa key in factotum",	"no key (q) found"
+
+#56. 4 ncsl lines
+	5,dh.c:99,102
+	5,dh.c:154,157
+	1 parameters
+ 	1. "no key (n) found",	"no proto=dsa key in factotum"
+
+#57. 4 ncsl lines
+	5,dh.c:154,157
+	5,dh.c:166,169
+	1 parameters
+ 	1. "no proto=dsa key in factotum",	"no key (p) found"
+
+#58. 4 ncsl lines
+	5,dh.c:154,157
+	5,dh.c:198,201
+	1 parameters
+ 	1. "no proto=dsa key in factotum",	"no key (x) found"
+
+#59. 4 ncsl lines
+	5,dh.c:108,111
+	5,dh.c:154,157
+	1 parameters
+ 	1. "no key (ek) found",	"no proto=dsa key in factotum"
+
+#60. 7 ncsl lines
+	9,netssh.c:662,668
+	9,netssh.c:957,963
+	0 parameters
+
+#61. 7 ncsl lines
+	5,dh.c:81,88
+	5,dh.c:147,154
+	2 parameters
+ 	1. "proto=rsa",	"proto=dsa"
+ 	2. "no proto=rsa key in factotum",	"no proto=dsa key in factotum"
+
+#62. 4 ncsl lines
+	12,ssh.c:398,401
+	12,ssh.c:417,420
+	1 parameters
+ 	1. cfd1,	cfd2
+
+#63. 4 ncsl lines
+	9,netssh.c:516,519
+	9,netssh.c:841,844
+	0 parameters
+
+#64. 4 ncsl lines
+	9,netssh.c:841,844
+	9,netssh.c:910,913
+	0 parameters
+
+#65. 3 ncsl lines
+	9,netssh.c:387,389
+	9,netssh.c:806,808
+	0 parameters
+
+#66. 4 ncsl lines
+	9,netssh.c:797,800
+	9,netssh.c:828,831
+	1 parameters
+ 	1. Connection,	Subchannel
+
+#67. 4 ncsl lines
+	9,netssh.c:758,761
+	9,netssh.c:868,871
+	1 parameters
+ 	1. "Key file collision",	nil
+
+#68. 4 ncsl lines
+	9,netssh.c:835,838
+	9,netssh.c:868,871
+	0 parameters
+
+#69. 3 ncsl lines
+	9,netssh.c:828,830
+	9,netssh.c:868,870
+	0 parameters
+
+#70. 3 ncsl lines
+	9,netssh.c:797,799
+	9,netssh.c:835,837
+	0 parameters
+
+#71. 6 ncsl lines
+	9,netssh.c:2351,2356
+	9,netssh.c:2412,2417
+	2 parameters
+ 	1. dataq,	reqq
+ 	2. datatl,	reqtl
+
+#72. 6 ncsl lines
+	9,netssh.c:1721,1726
+	9,netssh.c:1908,1913
+	1 parameters
+ 	1. remfd,	fd
+
+#73. 3 ncsl lines
+	6,dial.c:275,278
+	9,netssh.c:2425,2428
+	0 parameters
+
+#74. 3 ncsl lines
+	6,dial.c:141,144
+	9,netssh.c:2425,2428
+	0 parameters
+
+#75. 3 ncsl lines
+	6,dial.c:275,278
+	13,sshsession.c:147,150
+	0 parameters
+
+#76. 3 ncsl lines
+	6,dial.c:141,144
+	13,sshsession.c:147,150
+	0 parameters
+
+#77. 3 ncsl lines
+	7,dial.thread.c:177,180
+	13,sshsession.c:147,150
+	0 parameters
+
+#78. 3 ncsl lines
+	7,dial.thread.c:177,180
+	9,netssh.c:2425,2428
+	0 parameters
+
+#79. 3 ncsl lines
+	5,dh.c:795,798
+	9,netssh.c:2211,2214
+	0 parameters
+
+#80. 3 ncsl lines
+	5,dh.c:795,798
+	13,sshsession.c:147,150
+	0 parameters
+
+#81. 3 ncsl lines
+	5,dh.c:795,798
+	9,netssh.c:2425,2428
+	0 parameters
+
+#82. 3 ncsl lines
+	5,dh.c:795,798
+	7,dial.thread.c:177,180
+	0 parameters
+
+#83. 3 ncsl lines
+	5,dh.c:795,798
+	6,dial.c:275,278
+	0 parameters
+
+#84. 3 ncsl lines
+	5,dh.c:795,798
+	6,dial.c:141,144
+	0 parameters
+
+#85. 3 ncsl lines
+	7,dial.thread.c:177,180
+	9,netssh.c:2211,2214
+	0 parameters
+
+#86. 3 ncsl lines
+	6,dial.c:275,278
+	9,netssh.c:2211,2214
+	0 parameters
+
+#87. 3 ncsl lines
+	6,dial.c:141,144
+	9,netssh.c:2211,2214
+	0 parameters
+
+#88. 10 ncsl lines
+	0,cipher3des.c:28,39
+	2,cipherblowfish.c:42,53
+	3 parameters
+ 	1. encrypt3des,	encryptblowfish
+ 	2. des3CBCencrypt,	bfCBCencrypt
+ 	3. decrypt3des,	decryptblowfish
+
+#89. 5 ncsl lines
+	0,cipher3des.c:28,33
+	3,cipherrc4.c:24,29
+	1 parameters
+ 	1. encrypt3des,	encryptrc4
+
+#90. 8 ncsl lines
+	13,sshsession.c:59,66
+	13,sshsession.c:105,112
+	3 parameters
+ 	1. name,	p
+ 	2. OREAD,	ORDWR
+ 	3. "open data",	"open"
+
+#91. 3 ncsl lines
+	9,netssh.c:328,330
+	9,netssh.c:722,724
+	0 parameters
+
+#92. 3 ncsl lines
+	9,netssh.c:328,330
+	9,netssh.c:969,971
+	0 parameters
+
+#93. 4 ncsl lines
+	9,netssh.c:602,605
+	9,netssh.c:1410,1413
+	1 parameters
+ 	1. xconn,	chnum
+
+#94. 11 ncsl lines
+	9,netssh.c:879,890
+	9,netssh.c:1367,1378
+	4 parameters
+ 	1. sc,	ch
+ 	2. cnum,	n
+ 	3. "readdata",	"writedataproc"
+ 	4. "bad connection",	"Invalid connection"
+
+#95. 3 ncsl lines
+	9,netssh.c:732,734
+	9,netssh.c:878,880
+	0 parameters
+
+#96. 6 ncsl lines
+	9,netssh.c:2262,2267
+	9,netssh.c:2502,2507
+	1 parameters
+ 	1. "kex crypto algorithm mismatch (Established)",	"kex crypto algorithm mismatch (Initting)"
+
+#97. 4 ncsl lines
+	6,dial.c:357,361
+	7,dial.thread.c:361,365
+	0 parameters
+
+#98. 4 ncsl lines
+	6,dial.c:90,93
+	6,dial.c:100,103
+	1 parameters
+ 	1. "/net",	"/net.alt"
+
+#99. 4 ncsl lines
+	6,dial.c:100,103
+	7,dial.thread.c:106,109
+	1 parameters
+ 	1. "/net.alt",	"/net"
+
+#100. 4 ncsl lines
+	6,dial.c:90,93
+	7,dial.thread.c:116,119
+	1 parameters
+ 	1. "/net",	"/net.alt"
+
+#101. 4 ncsl lines
+	7,dial.thread.c:106,109
+	7,dial.thread.c:116,119
+	1 parameters
+ 	1. "/net",	"/net.alt"
+
+#102. 3 ncsl lines
+	9,netssh.c:2072,2074
+	9,netssh.c:2206,2208
+	0 parameters
+
+#103. 4 ncsl lines
+	9,netssh.c:2132,2135
+	9,netssh.c:2539,2542
+	1 parameters
+ 	1. SSH_MSG_KEXDH_INIT,	SSH_MSG_USERAUTH_SUCCESS
+
+#104. 4 ncsl lines
+	9,netssh.c:2132,2135
+	9,netssh.c:2166,2169
+	1 parameters
+ 	1. SSH_MSG_KEXDH_INIT,	SSH_MSG_SERVICE_REQUEST
+
+#105. 6 ncsl lines
+	9,netssh.c:1224,1229
+	9,netssh.c:1481,1486
+	1 parameters
+ 	1. ch,	sc
+
+#106. 3 ncsl lines
+	9,netssh.c:1224,1226
+	9,netssh.c:2324,2326
+	0 parameters
+
+#107. 6 ncsl lines
+	9,netssh.c:2310,2315
+	9,netssh.c:2319,2324
+	1 parameters
+ 	1. SSH_MSG_CHANNEL_OPEN_CONFIRMATION,	SSH_MSG_CHANNEL_OPEN_FAILURE
+
+#108. 3 ncsl lines
+	9,netssh.c:2449,2451
+	14,transport.c:189,191
+	0 parameters
+
+#109. 5 ncsl lines
+	9,netssh.c:619,623
+	9,netssh.c:750,754
+	0 parameters
+
+#110. 4 ncsl lines
+	9,netssh.c:137,141
+	9,netssh.c:155,159
+	0 parameters
+
+#111. 3 ncsl lines
+	9,netssh.c:904,906
+	9,netssh.c:922,924
+	0 parameters
+
+#112. 35 ncsl lines
+	5,dh.c:826,868
+	5,dh.c:835,877
+	(overlap; region has 5 nonoverlapping matching 7-ncsl-line segments)
+	10 parameters
+ 	1. 'A',	'B'
+ 	2. nc2siv,	ns2civ
+ 	3. 'B',	'C'
+ 	4. ns2civ,	nc2sek
+ 	5. 'C',	'D'
+ 	6. nc2sek,	ns2cek
+ 	7. 'D',	'E'
+ 	8. ns2cek,	nc2sik
+ 	9. 'E',	'F'
+ 	10. nc2sik,	ns2cik
+
+#113. 28 ncsl lines
+	5,dh.c:826,859
+	5,dh.c:844,877
+	(overlap; region has 2 nonoverlapping matching 14-ncsl-line segments)
+	8 parameters
+ 	1. 'A',	'C'
+ 	2. nc2siv,	nc2sek
+ 	3. 'B',	'D'
+ 	4. ns2civ,	ns2cek
+ 	5. 'C',	'E'
+ 	6. nc2sek,	nc2sik
+ 	7. 'D',	'F'
+ 	8. ns2cek,	ns2cik
+
+#114. 21 ncsl lines
+	5,dh.c:826,850
+	5,dh.c:853,877
+	6 parameters
+ 	1. 'A',	'D'
+ 	2. nc2siv,	ns2cek
+ 	3. 'B',	'E'
+ 	4. ns2civ,	nc2sik
+ 	5. 'C',	'F'
+ 	6. nc2sek,	ns2cik
+
+#115. 14 ncsl lines
+	5,dh.c:826,841
+	5,dh.c:862,877
+	4 parameters
+ 	1. 'A',	'E'
+ 	2. nc2siv,	nc2sik
+ 	3. 'B',	'F'
+ 	4. ns2civ,	ns2cik
+
+#116. 7 ncsl lines
+	5,dh.c:826,832
+	5,dh.c:871,877
+	2 parameters
+ 	1. 'A',	'F'
+ 	2. nc2siv,	ns2cik
+
+#117. 15 ncsl lines
+	0,cipher3des.c:1,17
+	3,cipherrc4.c:1,17
+	2 parameters
+ 	1. DES3state,	RC4state
+ 	2. init3des,	initrc4
+
+#118. 14 ncsl lines
+	2,cipherblowfish.c:1,16
+	3,cipherrc4.c:1,16
+	2 parameters
+ 	1. BFstate,	RC4state
+ 	2. initblowfish,	initrc4
+
+#119. 14 ncsl lines
+	0,cipher3des.c:1,16
+	2,cipherblowfish.c:1,16
+	2 parameters
+ 	1. DES3state,	BFstate
+ 	2. init3des,	initblowfish
+
+#120. 8 ncsl lines
+	0,cipher3des.c:1,8
+	1,cipheraes.c:1,8
+	0 parameters
+
+#121. 8 ncsl lines
+	1,cipheraes.c:1,8
+	3,cipherrc4.c:1,8
+	0 parameters
+
+#122. 8 ncsl lines
+	1,cipheraes.c:1,8
+	2,cipherblowfish.c:1,8
+	0 parameters
+
+#123. 8 ncsl lines
+	11,rsa2ssh2.c:1,8
+	14,transport.c:1,8
+	2 parameters
+ 	1. auth,	libsec
+ 	2. authsrv,	ip
+
+#124. 7 ncsl lines
+	1,cipheraes.c:1,7
+	14,transport.c:1,7
+	0 parameters
+
+#125. 7 ncsl lines
+	0,cipher3des.c:1,7
+	14,transport.c:1,7
+	0 parameters
+
+#126. 7 ncsl lines
+	3,cipherrc4.c:1,7
+	14,transport.c:1,7
+	0 parameters
+
+#127. 7 ncsl lines
+	2,cipherblowfish.c:1,7
+	14,transport.c:1,7
+	0 parameters
+
+#128. 7 ncsl lines
+	1,cipheraes.c:1,7
+	11,rsa2ssh2.c:1,7
+	1 parameters
+ 	1. libsec,	auth
+
+#129. 7 ncsl lines
+	0,cipher3des.c:1,7
+	11,rsa2ssh2.c:1,7
+	1 parameters
+ 	1. libsec,	auth
+
+#130. 7 ncsl lines
+	3,cipherrc4.c:1,7
+	11,rsa2ssh2.c:1,7
+	1 parameters
+ 	1. libsec,	auth
+
+#131. 7 ncsl lines
+	2,cipherblowfish.c:1,7
+	11,rsa2ssh2.c:1,7
+	1 parameters
+ 	1. libsec,	auth
+
+#132. 6 ncsl lines
+	2,cipherblowfish.c:1,6
+	7,dial.thread.c:4,9
+	1 parameters
+ 	1. mp,	ctype
+
+#133. 6 ncsl lines
+	3,cipherrc4.c:1,6
+	7,dial.thread.c:4,9
+	1 parameters
+ 	1. mp,	ctype
+
+#134. 6 ncsl lines
+	0,cipher3des.c:1,6
+	7,dial.thread.c:4,9
+	1 parameters
+ 	1. mp,	ctype
+
+#135. 6 ncsl lines
+	1,cipheraes.c:1,6
+	7,dial.thread.c:4,9
+	1 parameters
+ 	1. mp,	ctype
+
+#136. 6 ncsl lines
+	7,dial.thread.c:4,9
+	14,transport.c:1,6
+	1 parameters
+ 	1. ctype,	mp
+
+#137. 6 ncsl lines
+	7,dial.thread.c:4,9
+	11,rsa2ssh2.c:1,6
+	1 parameters
+ 	1. ctype,	mp
+
+#138. 3 ncsl lines
+	6,dial.c:4,6
+	7,dial.thread.c:4,6
+	0 parameters
+
+#139. 6 ncsl lines
+	5,dh.c:164,169
+	5,dh.c:178,183
+	2 parameters
+ 	1. " p=",	" alpha="
+ 	2. "no key (p) found",	"no key (g) found"
+
+#140. 6 ncsl lines
+	5,dh.c:164,169
+	5,dh.c:171,176
+	2 parameters
+ 	1. " p=",	" q="
+ 	2. "no key (p) found",	"no key (q) found"
+
+#141. 6 ncsl lines
+	5,dh.c:164,169
+	5,dh.c:185,190
+	2 parameters
+ 	1. " p=",	" key="
+ 	2. "no key (p) found",	"no key (y) found"
+
+#142. 6 ncsl lines
+	5,dh.c:97,102
+	5,dh.c:106,111
+	2 parameters
+ 	1. " n=",	" ek="
+ 	2. "no key (n) found",	"no key (ek) found"
+
+#143. 14 ncsl lines
+	1,cipheraes.c:69,83
+	1,cipheraes.c:77,91
+	(overlap; region has 2 nonoverlapping matching 7-ncsl-line segments)
+	6 parameters
+ 	1. cipheraes128,	cipheraes192
+ 	2. "aes128-cbc",	"aes192-cbc"
+ 	3. initaes128,	initaes192
+ 	4. cipheraes192,	cipheraes256
+ 	5. "aes192-cbc",	"aes256-cbc"
+ 	6. initaes192,	initaes256
+
+#144. 4 ncsl lines
+	9,netssh.c:176,179
+	13,sshsession.c:191,194
+	1 parameters
+ 	1. 'k',	'i'
+
+#145. 4 ncsl lines
+	12,ssh.c:467,470
+	13,sshsession.c:191,194
+	1 parameters
+ 	1. 'I',	'i'
+
+#146. 5 ncsl lines
+	9,netssh.c:626,630
+	9,netssh.c:648,652
+	1 parameters
+ 	1. Qlisten,	Qclone
+
+#147. 9 ncsl lines
+	9,netssh.c:1061,1070
+	9,netssh.c:1359,1368
+	2 parameters
+ 	1. "ssh-userath auth failed (not Established)",	nil
+ 	2. writectlproc,	writedataproc
+
+#148. 8 ncsl lines
+	9,netssh.c:2082,2089
+	9,netssh.c:2244,2251
+	1 parameters
+ 	1. SSH_MSG_NEWKEYS,	SSH_MSG_IGNORE
+
+#149. 6 ncsl lines
+	9,netssh.c:2244,2249
+	9,netssh.c:2519,2524
+	0 parameters
+
+#150. 3 ncsl lines
+	9,netssh.c:1782,1784
+	9,netssh.c:1789,1791
+	0 parameters
+
+#151. 12 ncsl lines
+	9,netssh.c:183,194
+	13,sshsession.c:208,219
+	4 parameters
+ 	1. mntpt,	shell
+ 	2. 's',	'S'
+ 	3. 'v',	't'
+ 	4. nokeyverify,	tflag
+
+#152. 8 ncsl lines
+	6,dial.c:431,438
+	7,dial.thread.c:432,439
+	0 parameters
+
+#153. 3 ncsl lines
+	12,ssh.c:163,165
+	12,ssh.c:167,171
+	0 parameters
+
+#154. 6 ncsl lines
+	8,esmprint.c:6,12
+	9,netssh.c:127,134
+	0 parameters
+
+#155. 3 ncsl lines
+	8,esmprint.c:6,8
+	9,netssh.c:143,145
+	0 parameters
+
+#156. 3 ncsl lines
+	9,netssh.c:2165,2167
+	9,netssh.c:2205,2207
+	0 parameters
+
+#157. 3 ncsl lines
+	9,netssh.c:2155,2157
+	9,netssh.c:2205,2207
+	0 parameters
+
+#158. 3 ncsl lines
+	9,netssh.c:2155,2157
+	9,netssh.c:2165,2167
+	0 parameters
+
+#159. 3 ncsl lines
+	6,dial.c:449,451
+	6,dial.c:475,477
+	0 parameters
+
+#160. 3 ncsl lines
+	6,dial.c:449,451
+	7,dial.thread.c:476,478
+	0 parameters
+
+#161. 3 ncsl lines
+	6,dial.c:475,477
+	7,dial.thread.c:450,452
+	0 parameters
+
+#162. 3 ncsl lines
+	7,dial.thread.c:450,452
+	7,dial.thread.c:476,478
+	0 parameters
+
+#163. 5 ncsl lines
+	5,dh.c:71,79
+	5,dh.c:137,145
+	1 parameters
+ 	1. "rsakey",	"dsskey"
+
+#164. 6 ncsl lines
+	9,netssh.c:1061,1067
+	9,netssh.c:1359,1365
+	2 parameters
+ 	1. "ssh-userath auth failed (not Established)",	nil
+ 	2. writectlproc,	writedataproc
+
+#165. 6 ncsl lines
+	9,netssh.c:2082,2087
+	9,netssh.c:2519,2524
+	0 parameters
+
+#166. 4 ncsl lines
+	10,pubkey.c:182,185
+	10,pubkey.c:195,198
+	1 parameters
+ 	1. "%s: new key file changed names? %s to %s\n",	"%s: error renaming %s to %s: %r\n"
+
+#167. 3 ncsl lines
+	5,dh.c:436,439
+	9,netssh.c:1802,1811
+	0 parameters
+
+#168. 5 ncsl lines
+	2,cipherblowfish.c:42,47
+	3,cipherrc4.c:24,29
+	1 parameters
+ 	1. encryptblowfish,	encryptrc4
+
+#169. 3 ncsl lines
+	14,transport.c:74,76
+	14,transport.c:110,112
+	0 parameters
+
+#170. 7 ncsl lines
+	6,dial.c:485,491
+	7,dial.thread.c:486,492
+	0 parameters
+
+#171. 6 ncsl lines
+	5,dh.c:171,176
+	5,dh.c:178,183
+	2 parameters
+ 	1. " q=",	" alpha="
+ 	2. "no key (q) found",	"no key (g) found"
+
+#172. 6 ncsl lines
+	5,dh.c:171,176
+	5,dh.c:185,190
+	2 parameters
+ 	1. " q=",	" key="
+ 	2. "no key (q) found",	"no key (y) found"
+
+#173. 4 ncsl lines
+	9,netssh.c:2709,2712
+	9,netssh.c:2741,2744
+	1 parameters
+ 	1. foundpka,	foundm1
+
+#174. 4 ncsl lines
+	9,netssh.c:2709,2712
+	9,netssh.c:2752,2755
+	1 parameters
+ 	1. foundpka,	foundm2
+
+#175. 4 ncsl lines
+	9,netssh.c:2730,2733
+	9,netssh.c:2741,2744
+	1 parameters
+ 	1. foundc2,	foundm1
+
+#176. 4 ncsl lines
+	9,netssh.c:2730,2733
+	9,netssh.c:2752,2755
+	1 parameters
+ 	1. foundc2,	foundm2
+
+#177. 4 ncsl lines
+	9,netssh.c:2720,2723
+	9,netssh.c:2741,2744
+	1 parameters
+ 	1. foundc1,	foundm1
+
+#178. 4 ncsl lines
+	9,netssh.c:2720,2723
+	9,netssh.c:2752,2755
+	1 parameters
+ 	1. foundc1,	foundm2
+
+#179. 4 ncsl lines
+	9,netssh.c:2698,2701
+	9,netssh.c:2741,2744
+	1 parameters
+ 	1. foundk,	foundm1
+
+#180. 4 ncsl lines
+	9,netssh.c:2698,2701
+	9,netssh.c:2752,2755
+	1 parameters
+ 	1. foundk,	foundm2
+
+#181. 3 ncsl lines
+	6,dial.c:426,428
+	7,dial.thread.c:426,428
+	0 parameters
+
+#182. 7 ncsl lines
+	9,netssh.c:1233,1239
+	9,netssh.c:1489,1495
+	1 parameters
+ 	1. Closed,	Closing
+
+#183. 7 ncsl lines
+	9,netssh.c:1443,1449
+	9,netssh.c:1489,1495
+	0 parameters
+
+#184. 4 ncsl lines
+	9,netssh.c:334,337
+	9,netssh.c:693,696
+	1 parameters
+ 	1. Qclone,	Qstatus
+
+#185. 4 ncsl lines
+	13,sshsession.c:365,368
+	13,sshsession.c:373,376
+	0 parameters
+
+#186. 3 ncsl lines
+	9,netssh.c:1245,1247
+	9,netssh.c:2208,2210
+	0 parameters
+
+#187. 5 ncsl lines
+	14,transport.c:129,134
+	14,transport.c:174,183
+	1 parameters
+ 	1. finish_packet,	undo_packet
+
+#188. 6 ncsl lines
+	9,netssh.c:676,681
+	9,netssh.c:709,715
+	2 parameters
+ 	1. Qlocal,	Qtcp
+ 	2. "::!0\n",	"-1\n"
+
+#189. 3 ncsl lines
+	9,netssh.c:2894,2896
+	9,netssh.c:2911,2913
+	0 parameters
+
+#190. 3 ncsl lines
+	9,netssh.c:2880,2882
+	9,netssh.c:2911,2913
+	0 parameters
+
+#191. 3 ncsl lines
+	5,dh.c:109,111
+	5,dh.c:122,124
+	0 parameters
+
+#192. 3 ncsl lines
+	5,dh.c:122,124
+	5,dh.c:199,201
+	0 parameters
+
+#193. 3 ncsl lines
+	5,dh.c:122,124
+	5,dh.c:155,157
+	0 parameters
+
+#194. 3 ncsl lines
+	5,dh.c:122,124
+	5,dh.c:188,190
+	0 parameters
+
+#195. 3 ncsl lines
+	5,dh.c:122,124
+	5,dh.c:181,183
+	0 parameters
+
+#196. 3 ncsl lines
+	5,dh.c:122,124
+	5,dh.c:174,176
+	0 parameters
+
+#197. 3 ncsl lines
+	5,dh.c:100,102
+	5,dh.c:122,124
+	0 parameters
+
+#198. 3 ncsl lines
+	5,dh.c:122,124
+	5,dh.c:167,169
+	0 parameters
+
+#199. 3 ncsl lines
+	5,dh.c:289,291
+	5,dh.c:424,426
+	0 parameters
+
+#200. 3 ncsl lines
+	6,dial.c:364,366
+	6,dial.c:420,422
+	0 parameters
+
+#201. 4 ncsl lines
+	10,pubkey.c:35,38
+	10,pubkey.c:49,52
+	0 parameters
+
+#202. 4 ncsl lines
+	9,netssh.c:591,594
+	9,netssh.c:1220,1223
+	0 parameters
+
+#203. 3 ncsl lines
+	9,netssh.c:650,652
+	9,netssh.c:954,956
+	0 parameters
+
+#204. 3 ncsl lines
+	9,netssh.c:628,630
+	9,netssh.c:954,956
+	0 parameters
+
+#205. 5 ncsl lines
+	9,netssh.c:898,902
+	9,netssh.c:916,920
+	1 parameters
+ 	1. "sending EOF1 to channel listener",	"sending EOF2 to channel listener"
+
+#206. 4 ncsl lines
+	9,netssh.c:1056,1059
+	9,netssh.c:1819,1822
+	1 parameters
+ 	1. "sleeping for auth",	"awaiting establishment"
+
+#207. 3 ncsl lines
+	9,netssh.c:671,673
+	9,netssh.c:869,871
+	0 parameters
+
+#208. 3 ncsl lines
+	9,netssh.c:671,673
+	9,netssh.c:836,838
+	0 parameters
+
+#209. 17 ncsl lines
+	9,netssh.c:2709,2726
+	9,netssh.c:2720,2737
+	(overlap; region has 2 nonoverlapping matching 10-ncsl-line segments)
+	4 parameters
+ 	1. foundpka,	foundc1
+ 	2. pkalg,	ncscrypt
+ 	3. foundc1,	foundc2
+ 	4. ncscrypt,	nsccrypt
+
+#210. 9 ncsl lines
+	9,netssh.c:2720,2728
+	9,netssh.c:2730,2739
+	3 parameters
+ 	1. foundc1,	foundc2
+ 	2. ncscrypt,	nsccrypt
+ 	3. cryptos,	macnames
+
+#211. 4 ncsl lines
+	9,netssh.c:488,492
+	9,netssh.c:1264,1268
+	1 parameters
+ 	1. "stlischan",	"writereqremproc"
+
+#212. 22 ncsl lines
+	9,netssh.c:2614,2637
+	9,netssh.c:2627,2650
+	(overlap; region has 2 nonoverlapping matching 12-ncsl-line segments)
+	8 parameters
+ 	1. foundpka,	foundc1
+ 	2. "host key algs not in pkas",	"c2s crypto algs not in cryptos"
+ 	3. pkalg,	ncscrypt
+ 	4. "received C2S crypto algs: %s",	"received S2C crypto algs: %s"
+ 	5. foundc1,	foundc2
+ 	6. "c2s crypto algs not in cryptos",	"s2c crypto algs not in cryptos"
+ 	7. ncscrypt,	nsccrypt
+ 	8. "received S2C crypto algs: %s",	"received C2S MAC algs: %s"
+
+#213. 4 ncsl lines
+	9,netssh.c:516,519
+	9,netssh.c:910,913
+	0 parameters
+
+#214. 4 ncsl lines
+	9,netssh.c:758,761
+	9,netssh.c:835,838
+	1 parameters
+ 	1. "Key file collision",	nil
+
+#215. 4 ncsl lines
+	9,netssh.c:418,421
+	9,netssh.c:899,902
+	1 parameters
+ 	1. "top level listen conn poisoned",	nil
+
+#216. 4 ncsl lines
+	9,netssh.c:418,421
+	9,netssh.c:917,920
+	1 parameters
+ 	1. "top level listen conn poisoned",	nil
+
+#217. 8 ncsl lines
+	6,dial.c:332,340
+	7,dial.thread.c:332,340
+	1 parameters
+ 	1. rv,	dfd
+
+#218. 3 ncsl lines
+	9,netssh.c:1802,1811
+	9,netssh.c:1830,1834
+	0 parameters
+
+#219. 3 ncsl lines
+	5,dh.c:436,439
+	9,netssh.c:1830,1834
+	0 parameters
+
+#220. 3 ncsl lines
+	6,dial.c:141,144
+	7,dial.thread.c:177,180
+	0 parameters
+
+#221. 3 ncsl lines
+	6,dial.c:141,144
+	6,dial.c:275,278
+	0 parameters
+
+#222. 3 ncsl lines
+	9,netssh.c:1693,1695
+	12,ssh.c:265,267
+	0 parameters
+
+#223. 3 ncsl lines
+	7,dial.thread.c:158,161
+	13,sshsession.c:117,120
+	0 parameters
+
+#224. 3 ncsl lines
+	9,netssh.c:1495,1498
+	9,netssh.c:2554,2557
+	0 parameters
+
+#225. 3 ncsl lines
+	9,netssh.c:2554,2557
+	13,sshsession.c:328,331
+	0 parameters
+
+#226. 3 ncsl lines
+	6,dial.c:164,167
+	12,ssh.c:259,263
+	0 parameters
+
+#227. 3 ncsl lines
+	6,dial.c:164,167
+	12,ssh.c:112,116
+	0 parameters
+
+#228. 3 ncsl lines
+	12,ssh.c:112,116
+	12,ssh.c:259,263
+	0 parameters
+
+#229. 3 ncsl lines
+	6,dial.c:164,167
+	13,sshsession.c:302,305
+	0 parameters
+
+#230. 3 ncsl lines
+	12,ssh.c:112,116
+	13,sshsession.c:302,305
+	0 parameters
+
+#231. 3 ncsl lines
+	7,dial.thread.c:149,152
+	12,ssh.c:112,116
+	0 parameters
+
+#232. 3 ncsl lines
+	6,dial.c:164,167
+	7,dial.thread.c:149,152
+	0 parameters
+
+#233. 3 ncsl lines
+	9,netssh.c:2208,2210
+	12,ssh.c:309,311
+	0 parameters
+
+#234. 3 ncsl lines
+	9,netssh.c:1245,1247
+	12,ssh.c:309,311
+	0 parameters
+
+#235. 8 ncsl lines
+	9,netssh.c:1062,1070
+	9,netssh.c:1253,1261
+	1 parameters
+ 	1. writectlproc,	writereqremproc
+
+#236. 8 ncsl lines
+	9,netssh.c:1253,1261
+	9,netssh.c:1360,1368
+	1 parameters
+ 	1. writereqremproc,	writedataproc
+
+#237. 4 ncsl lines
+	9,netssh.c:392,396
+	9,netssh.c:1360,1364
+	1 parameters
+ 	1. stlisconn,	writedataproc
+
+#238. 4 ncsl lines
+	9,netssh.c:392,396
+	9,netssh.c:1062,1066
+	1 parameters
+ 	1. stlisconn,	writectlproc
+
+#239. 4 ncsl lines
+	9,netssh.c:164,168
+	13,sshsession.c:178,182
+	1 parameters
+ 	1. threadmain,	main
+
+#240. 4 ncsl lines
+	11,rsa2ssh2.c:20,24
+	13,sshsession.c:178,182
+	0 parameters
+
+#241. 6 ncsl lines
+	1,cipheraes.c:47,53
+	1,cipheraes.c:57,63
+	1 parameters
+ 	1. encryptaes,	decryptaes
+
+#242. 4 ncsl lines
+	7,dial.thread.c:140,144
+	7,dial.thread.c:228,233
+	1 parameters
+ 	1. freedest,	intrcallprocs
+
+#243. 4 ncsl lines
+	9,netssh.c:1758,1762
+	9,netssh.c:2065,2069
+	1 parameters
+ 	1. hangupconn,	establish
+
+#244. 3 ncsl lines
+	9,netssh.c:1672,1674
+	9,netssh.c:3194,3196
+	0 parameters
+
+#245. 3 ncsl lines
+	9,netssh.c:1672,1674
+	9,netssh.c:3188,3190
+	0 parameters
+
+#246. 3 ncsl lines
+	9,netssh.c:666,668
+	9,netssh.c:1701,1703
+	0 parameters
+
+#247. 3 ncsl lines
+	9,netssh.c:666,668
+	12,ssh.c:218,220
+	0 parameters
+
+#248. 3 ncsl lines
+	9,netssh.c:1701,1703
+	12,ssh.c:218,220
+	0 parameters
+
+#249. 3 ncsl lines
+	9,netssh.c:961,963
+	9,netssh.c:1701,1703
+	0 parameters
+
+#250. 3 ncsl lines
+	9,netssh.c:961,963
+	12,ssh.c:218,220
+	0 parameters
+
+#251. 3 ncsl lines
+	9,netssh.c:971,973
+	12,ssh.c:218,220
+	0 parameters
+
+#252. 3 ncsl lines
+	9,netssh.c:971,973
+	9,netssh.c:1701,1703
+	0 parameters
+
+#253. 3 ncsl lines
+	9,netssh.c:724,726
+	9,netssh.c:1701,1703
+	0 parameters
+
+#254. 3 ncsl lines
+	9,netssh.c:724,726
+	12,ssh.c:218,220
+	0 parameters
+
+#255. 3 ncsl lines
+	9,netssh.c:380,382
+	12,ssh.c:218,220
+	0 parameters
+
+#256. 3 ncsl lines
+	9,netssh.c:380,382
+	9,netssh.c:1701,1703
+	0 parameters
+
+#257. 42 ncsl lines
+	6,dial.c:68,124
+	7,dial.thread.c:85,140
+	0 parameters
+
+#258. 5 ncsl lines
+	13,sshsession.c:85,89
+	13,sshsession.c:171,175
+	1 parameters
+ 	1. "capuse",	"fork"
+
+#259. 4 ncsl lines
+	13,sshsession.c:85,88
+	13,sshsession.c:247,250
+	1 parameters
+ 	1. "capuse",	"read cap"
+
+#260. 3 ncsl lines
+	13,sshsession.c:79,81
+	13,sshsession.c:85,87
+	0 parameters
+
+#261. 3 ncsl lines
+	9,netssh.c:312,315
+	9,netssh.c:923,926
+	0 parameters
+
+#262. 3 ncsl lines
+	9,netssh.c:391,394
+	9,netssh.c:923,926
+	0 parameters
+
+#263. 3 ncsl lines
+	9,netssh.c:923,926
+	9,netssh.c:1252,1255
+	0 parameters
+
+#264. 3 ncsl lines
+	9,netssh.c:476,479
+	9,netssh.c:923,926
+	0 parameters
+
+#265. 4 ncsl lines
+	6,dial.c:420,424
+	7,dial.thread.c:419,424
+	1 parameters
+ 	1. rv,	dfd
+
+#266. 4 ncsl lines
+	9,netssh.c:1186,1189
+	9,netssh.c:1492,1495
+	0 parameters
+
+#267. 4 ncsl lines
+	9,netssh.c:1186,1189
+	9,netssh.c:1236,1239
+	0 parameters
+
+#268. 4 ncsl lines
+	9,netssh.c:1186,1189
+	9,netssh.c:1446,1449
+	0 parameters
+
+#269. 9 ncsl lines
+	6,dial.c:442,452
+	7,dial.thread.c:443,453
+	0 parameters
+
+#270. 4 ncsl lines
+	9,netssh.c:2595,2598
+	9,netssh.c:2634,2637
+	1 parameters
+ 	1. "received KEX algs: %s",	"received S2C crypto algs: %s"
+
+#271. 4 ncsl lines
+	9,netssh.c:2595,2598
+	9,netssh.c:2621,2624
+	1 parameters
+ 	1. "received KEX algs: %s",	"received C2S crypto algs: %s"
+
+#272. 4 ncsl lines
+	9,netssh.c:2595,2598
+	9,netssh.c:2660,2663
+	1 parameters
+ 	1. "received KEX algs: %s",	"received S2C MAC algs: %s"
+
+#273. 4 ncsl lines
+	9,netssh.c:2595,2598
+	9,netssh.c:2647,2650
+	1 parameters
+ 	1. "received KEX algs: %s",	"received C2S MAC algs: %s"
+
+#274. 4 ncsl lines
+	9,netssh.c:2595,2598
+	9,netssh.c:2608,2611
+	1 parameters
+ 	1. "received KEX algs: %s",	"received host key algs: %s"
+
+#275. 5 ncsl lines
+	9,netssh.c:1123,1129
+	9,netssh.c:1158,1163
+	1 parameters
+ 	1. "connect handshake failed: ",	"accept handshake failed: "
+
+#276. 3 ncsl lines
+	9,netssh.c:591,593
+	9,netssh.c:1356,1358
+	0 parameters
+
+#277. 3 ncsl lines
+	9,netssh.c:1220,1222
+	9,netssh.c:1356,1358
+	0 parameters
+
+#278. 3 ncsl lines
+	9,netssh.c:658,660
+	9,netssh.c:869,871
+	0 parameters
+
+#279. 3 ncsl lines
+	9,netssh.c:658,660
+	9,netssh.c:671,673
+	0 parameters
+
+#280. 3 ncsl lines
+	9,netssh.c:628,630
+	9,netssh.c:869,871
+	0 parameters
+
+#281. 3 ncsl lines
+	9,netssh.c:628,630
+	9,netssh.c:671,673
+	0 parameters
+
+#282. 3 ncsl lines
+	9,netssh.c:628,630
+	9,netssh.c:658,660
+	0 parameters
+
+#283. 3 ncsl lines
+	9,netssh.c:650,652
+	9,netssh.c:658,660
+	0 parameters
+
+#284. 3 ncsl lines
+	9,netssh.c:650,652
+	9,netssh.c:869,871
+	0 parameters
+
+#285. 3 ncsl lines
+	9,netssh.c:650,652
+	9,netssh.c:671,673
+	0 parameters
+
+#286. 3 ncsl lines
+	9,netssh.c:869,871
+	9,netssh.c:954,956
+	0 parameters
+
+#287. 3 ncsl lines
+	9,netssh.c:671,673
+	9,netssh.c:954,956
+	0 parameters
+
+#288. 3 ncsl lines
+	9,netssh.c:658,660
+	9,netssh.c:954,956
+	0 parameters
+
+#289. 3 ncsl lines
+	9,netssh.c:658,660
+	9,netssh.c:836,838
+	0 parameters
+
+#290. 3 ncsl lines
+	9,netssh.c:836,838
+	9,netssh.c:954,956
+	0 parameters
+
+#291. 3 ncsl lines
+	9,netssh.c:650,652
+	9,netssh.c:836,838
+	0 parameters
+
+#292. 3 ncsl lines
+	9,netssh.c:628,630
+	9,netssh.c:836,838
+	0 parameters
+
+#293. 3 ncsl lines
+	5,dh.c:280,282
+	5,dh.c:418,420
+	0 parameters
+
+#294. 11 ncsl lines
+	9,netssh.c:488,499
+	9,netssh.c:882,893
+	2 parameters
+ 	1. "stlischan",	"readdata"
+ 	2. "bad channel",	"bad connection"
+
+#295. 8 ncsl lines
+	9,netssh.c:488,496
+	9,netssh.c:1370,1378
+	2 parameters
+ 	1. "stlischan",	"writedataproc"
+ 	2. "bad channel",	"Invalid connection"
+
+#296. 4 ncsl lines
+	9,netssh.c:882,886
+	9,netssh.c:1264,1268
+	1 parameters
+ 	1. "readdata",	"writereqremproc"
+
+#297. 4 ncsl lines
+	9,netssh.c:1264,1268
+	9,netssh.c:1370,1374
+	1 parameters
+ 	1. "writereqremproc",	"writedataproc"
+
+#298. 10 ncsl lines
+	9,netssh.c:473,483
+	9,netssh.c:1249,1259
+	4 parameters
+ 	1. closeioproc,	free
+ 	2. io,	buf
+ 	3. stlischan,	writereqremproc
+ 	4. p2,	p
+
+#299. 3 ncsl lines
+	9,netssh.c:2773,2779
+	9,netssh.c:3073,3077
+	0 parameters
+
+#300. 3 ncsl lines
+	5,dh.c:617,620
+	9,netssh.c:1802,1811
+	0 parameters
+
+#301. 3 ncsl lines
+	5,dh.c:617,620
+	9,netssh.c:1830,1834
+	0 parameters
+
+#302. 3 ncsl lines
+	6,dial.c:332,335
+	6,dial.c:421,424
+	0 parameters
+
+#303. 3 ncsl lines
+	5,dh.c:436,439
+	5,dh.c:617,620
+	0 parameters
+
+#304. 3 ncsl lines
+	5,dh.c:575,578
+	9,netssh.c:1802,1811
+	0 parameters
+
+#305. 3 ncsl lines
+	5,dh.c:575,578
+	9,netssh.c:1830,1834
+	0 parameters
+
+#306. 3 ncsl lines
+	5,dh.c:436,439
+	5,dh.c:789,792
+	0 parameters
+
+#307. 3 ncsl lines
+	5,dh.c:789,792
+	9,netssh.c:1802,1811
+	0 parameters
+
+#308. 3 ncsl lines
+	5,dh.c:789,792
+	9,netssh.c:1830,1834
+	0 parameters
+
+#309. 3 ncsl lines
+	5,dh.c:296,304
+	5,dh.c:428,431
+	0 parameters
+
+#310. 3 ncsl lines
+	7,dial.thread.c:332,335
+	7,dial.thread.c:420,424
+	0 parameters
+
+#311. 3 ncsl lines
+	9,netssh.c:1227,1229
+	9,netssh.c:2365,2367
+	0 parameters
+
+#312. 4 ncsl lines
+	1,cipheraes.c:12,16
+	2,cipherblowfish.c:10,14
+	1 parameters
+ 	1. AESstate,	BFstate
+
+#313. 4 ncsl lines
+	0,cipher3des.c:10,14
+	1,cipheraes.c:12,16
+	1 parameters
+ 	1. DES3state,	AESstate
+
+#314. 4 ncsl lines
+	1,cipheraes.c:12,16
+	3,cipherrc4.c:10,14
+	1 parameters
+ 	1. AESstate,	RC4state
+
+#315. 3 ncsl lines
+	10,pubkey.c:37,39
+	14,transport.c:207,209
+	0 parameters
+
+#316. 3 ncsl lines
+	10,pubkey.c:37,39
+	10,pubkey.c:217,219
+	0 parameters
+
+#317. 3 ncsl lines
+	10,pubkey.c:217,219
+	14,transport.c:207,209
+	0 parameters
+
+#318. 4 ncsl lines
+	6,dial.c:342,346
+	7,dial.thread.c:344,348
+	0 parameters
+
+#319. 4 ncsl lines
+	5,dh.c:108,111
+	5,dh.c:198,201
+	1 parameters
+ 	1. "no key (ek) found",	"no key (x) found"
+
+#320. 4 ncsl lines
+	5,dh.c:187,190
+	5,dh.c:198,201
+	1 parameters
+ 	1. "no key (y) found",	"no key (x) found"
+
+#321. 4 ncsl lines
+	5,dh.c:180,183
+	5,dh.c:198,201
+	1 parameters
+ 	1. "no key (g) found",	"no key (x) found"
+
+#322. 4 ncsl lines
+	5,dh.c:173,176
+	5,dh.c:198,201
+	1 parameters
+ 	1. "no key (q) found",	"no key (x) found"
+
+#323. 4 ncsl lines
+	5,dh.c:99,102
+	5,dh.c:198,201
+	1 parameters
+ 	1. "no key (n) found",	"no key (x) found"
+
+#324. 4 ncsl lines
+	5,dh.c:166,169
+	5,dh.c:198,201
+	1 parameters
+ 	1. "no key (p) found",	"no key (x) found"
+
+#325. 6 ncsl lines
+	5,dh.c:487,492
+	5,dh.c:707,712
+	0 parameters
+
+#326. 3 ncsl lines
+	9,netssh.c:2269,2271
+	9,netssh.c:2509,2511
+	0 parameters
+
+#327. 11 ncsl lines
+	1,cipheraes.c:29,41
+	1,cipheraes.c:35,47
+	(overlap; region has 3 nonoverlapping matching 5-ncsl-line segments)
+	4 parameters
+ 	1. initaes128,	initaes192
+ 	2. 128,	192
+ 	3. initaes192,	initaes256
+ 	4. 192,	256
+
+#328. 6 ncsl lines
+	1,cipheraes.c:29,35
+	1,cipheraes.c:41,47
+	2 parameters
+ 	1. initaes128,	initaes256
+ 	2. 128,	256
+
+#329. 5 ncsl lines
+	5,dh.c:243,248
+	5,dh.c:387,392
+	1 parameters
+ 	1. rsa_sign,	dss_sign
+
+#330. 5 ncsl lines
+	5,dh.c:204,209
+	5,dh.c:371,376
+	1 parameters
+ 	1. rsa_ks,	dss_ks
+
+#331. 4 ncsl lines
+	9,netssh.c:2074,2078
+	9,netssh.c:2229,2233
+	1 parameters
+ 	1. negotiating,	established
+
+#332. 4 ncsl lines
+	6,dial.c:204,208
+	7,dial.thread.c:275,284
+	1 parameters
+ 	1. reap,	dialmulti
+
+#333. 4 ncsl lines
+	6,dial.c:216,220
+	6,dial.c:294,303
+	1 parameters
+ 	1. fillinds,	dialmulti
+
+#334. 6 ncsl lines
+	9,netssh.c:597,603
+	9,netssh.c:924,930
+	2 parameters
+ 	1. stread,	stwrite
+ 	2. sc,	ch
+
+#335. 4 ncsl lines
+	9,netssh.c:313,317
+	9,netssh.c:597,601
+	1 parameters
+ 	1. stopen,	stread
+
+#336. 4 ncsl lines
+	12,ssh.c:30,38
+	12,ssh.c:80,84
+	1 parameters
+ 	1. shutdown,	parseargs
+
+#337. 4 ncsl lines
+	9,netssh.c:2212,2216
+	9,netssh.c:2440,2444
+	1 parameters
+ 	1. nochans,	reader0
+
+#338. 4 ncsl lines
+	1,cipheraes.c:57,61
+	2,cipherblowfish.c:49,53
+	1 parameters
+ 	1. decryptaes,	decryptblowfish
+
+#339. 4 ncsl lines
+	1,cipheraes.c:47,51
+	2,cipherblowfish.c:49,53
+	1 parameters
+ 	1. encryptaes,	decryptblowfish
+
+#340. 4 ncsl lines
+	0,cipher3des.c:35,39
+	1,cipheraes.c:57,61
+	1 parameters
+ 	1. decrypt3des,	decryptaes
+
+#341. 4 ncsl lines
+	0,cipher3des.c:35,39
+	1,cipheraes.c:47,51
+	1 parameters
+ 	1. decrypt3des,	encryptaes
+
+#342. 4 ncsl lines
+	6,dial.c:142,146
+	7,dial.thread.c:228,233
+	1 parameters
+ 	1. freedest,	intrcallprocs
+
+#343. 4 ncsl lines
+	6,dial.c:142,146
+	7,dial.thread.c:140,144
+	0 parameters
+
+#344. 4 ncsl lines
+	0,cipher3des.c:27,31
+	13,sshsession.c:146,150
+	1 parameters
+ 	1. cs,	0
+
+#345. 4 ncsl lines
+	6,dial.c:522,529
+	13,sshsession.c:146,150
+	1 parameters
+ 	1. sl,	0
+
+#346. 4 ncsl lines
+	7,dial.thread.c:516,523
+	13,sshsession.c:146,150
+	1 parameters
+ 	1. sl,	0
+
+#347. 4 ncsl lines
+	0,cipher3des.c:27,31
+	6,dial.c:522,529
+	1 parameters
+ 	1. cs,	sl
+
+#348. 4 ncsl lines
+	0,cipher3des.c:27,31
+	7,dial.thread.c:516,523
+	1 parameters
+ 	1. cs,	sl
+
+#349. 4 ncsl lines
+	9,netssh.c:1460,1463
+	9,netssh.c:2387,2390
+	1 parameters
+ 	1. sc,	ch
+
+#350. 3 ncsl lines
+	9,netssh.c:579,581
+	9,netssh.c:865,867
+	0 parameters
+
+#351. 4 ncsl lines
+	9,netssh.c:2359,2362
+	9,netssh.c:2397,2400
+	1 parameters
+ 	1. SSH_MSG_CHANNEL_EOF,	SSH_MSG_CHANNEL_REQUEST
+
+#352. 5 ncsl lines
+	9,netssh.c:175,179
+	12,ssh.c:466,470
+	1 parameters
+ 	1. 'k',	'I'
+
+#353. 4 ncsl lines
+	9,netssh.c:377,380
+	9,netssh.c:706,709
+	0 parameters
+
+#354. 3 ncsl lines
+	9,netssh.c:1922,1924
+	9,netssh.c:1995,1997
+	0 parameters
+
+#355. 3 ncsl lines
+	6,dial.c:350,352
+	6,dial.c:374,376
+	0 parameters
+
+#356. 3 ncsl lines
+	6,dial.c:350,352
+	7,dial.thread.c:377,379
+	0 parameters
+
+#357. 9 ncsl lines
+	9,netssh.c:1086,1095
+	9,netssh.c:1274,1282
+	1 parameters
+ 	1. Numbsz,	10
+
+#358. 3 ncsl lines
+	9,netssh.c:1086,1088
+	9,netssh.c:1379,1381
+	0 parameters
+
+#359. 4 ncsl lines
+	9,netssh.c:593,596
+	9,netssh.c:866,869
+	1 parameters
+ 	1. p,	d
+
+#360. 3 ncsl lines
+	9,netssh.c:808,810
+	9,netssh.c:819,821
+	0 parameters
+
+#361. 7 ncsl lines
+	9,netssh.c:2876,2882
+	9,netssh.c:2890,2896
+	0 parameters
+
+#362. 4 ncsl lines
+	6,dial.c:61,65
+	7,dial.thread.c:64,67
+	1 parameters
+ 	1. winner,	naddrs
+
+#363. 7 ncsl lines
+	12,ssh.c:24,30
+	13,sshsession.c:26,32
+	2 parameters
+ 	1. "usage: %s [-dkKmr] [-l user] [-n dir] [-z attr=val] addr ",	"usage: %s [-i id] [-s shell] [-r dir] [-R dir] [-S srvpt] "
+ 	2. "[cmd [args]]\n",	"[-n ns] [-t] [netdir]\n"
+
+#364. 3 ncsl lines
+	11,rsa2ssh2.c:15,17
+	13,sshsession.c:26,28
+	0 parameters
+
+#365. 3 ncsl lines
+	11,rsa2ssh2.c:15,17
+	12,ssh.c:24,26
+	0 parameters
+
+#366. 3 ncsl lines
+	12,ssh.c:255,257
+	12,ssh.c:296,299
+	0 parameters
+
+#367. 3 ncsl lines
+	9,netssh.c:2227,2229
+	9,netssh.c:2378,2380
+	0 parameters
+
+#368. 3 ncsl lines
+	5,dh.c:572,574
+	9,netssh.c:1204,1206
+	0 parameters
+
+#369. 3 ncsl lines
+	5,dh.c:572,574
+	9,netssh.c:1356,1358
+	0 parameters
+
+#370. 3 ncsl lines
+	9,netssh.c:1204,1206
+	9,netssh.c:1356,1358
+	0 parameters
+
+#371. 3 ncsl lines
+	9,netssh.c:591,593
+	9,netssh.c:1204,1206
+	0 parameters
+
+#372. 3 ncsl lines
+	9,netssh.c:1204,1206
+	9,netssh.c:1220,1222
+	0 parameters
+
+#373. 3 ncsl lines
+	5,dh.c:572,574
+	9,netssh.c:591,593
+	0 parameters
+
+#374. 3 ncsl lines
+	5,dh.c:572,574
+	9,netssh.c:1220,1222
+	0 parameters
+
+#375. 3 ncsl lines
+	5,dh.c:572,574
+	5,dh.c:784,786
+	0 parameters
+
+#376. 3 ncsl lines
+	5,dh.c:784,786
+	9,netssh.c:591,593
+	0 parameters
+
+#377. 3 ncsl lines
+	5,dh.c:784,786
+	9,netssh.c:1220,1222
+	0 parameters
+
+#378. 3 ncsl lines
+	5,dh.c:784,786
+	9,netssh.c:1356,1358
+	0 parameters
+
+#379. 4 ncsl lines
+	9,netssh.c:960,963
+	9,netssh.c:970,973
+	1 parameters
+ 	1. "deferredinit failed",	nil
+
+#380. 4 ncsl lines
+	9,netssh.c:723,726
+	9,netssh.c:960,963
+	1 parameters
+ 	1. nil,	"deferredinit failed"
+
+#381. 4 ncsl lines
+	9,netssh.c:665,668
+	9,netssh.c:723,726
+	1 parameters
+ 	1. "deferredinit failed",	nil
+
+#382. 4 ncsl lines
+	9,netssh.c:665,668
+	9,netssh.c:970,973
+	1 parameters
+ 	1. "deferredinit failed",	nil
+
+#383. 3 ncsl lines
+	9,netssh.c:723,725
+	9,netssh.c:836,838
+	0 parameters
+
+#384. 3 ncsl lines
+	9,netssh.c:836,838
+	9,netssh.c:970,972
+	0 parameters
+
+#385. 3 ncsl lines
+	9,netssh.c:723,725
+	9,netssh.c:954,956
+	0 parameters
+
+#386. 3 ncsl lines
+	9,netssh.c:954,956
+	9,netssh.c:970,972
+	0 parameters
+
+#387. 3 ncsl lines
+	9,netssh.c:650,652
+	9,netssh.c:723,725
+	0 parameters
+
+#388. 3 ncsl lines
+	9,netssh.c:650,652
+	9,netssh.c:970,972
+	0 parameters
+
+#389. 3 ncsl lines
+	9,netssh.c:628,630
+	9,netssh.c:723,725
+	0 parameters
+
+#390. 3 ncsl lines
+	9,netssh.c:628,630
+	9,netssh.c:970,972
+	0 parameters
+
+#391. 3 ncsl lines
+	9,netssh.c:658,660
+	9,netssh.c:723,725
+	0 parameters
+
+#392. 3 ncsl lines
+	9,netssh.c:658,660
+	9,netssh.c:970,972
+	0 parameters
+
+#393. 3 ncsl lines
+	9,netssh.c:723,725
+	9,netssh.c:869,871
+	0 parameters
+
+#394. 3 ncsl lines
+	9,netssh.c:869,871
+	9,netssh.c:970,972
+	0 parameters
+
+#395. 3 ncsl lines
+	9,netssh.c:671,673
+	9,netssh.c:723,725
+	0 parameters
+
+#396. 3 ncsl lines
+	9,netssh.c:671,673
+	9,netssh.c:970,972
+	0 parameters
+
+#397. 4 ncsl lines
+	5,dh.c:166,169
+	5,dh.c:187,190
+	1 parameters
+ 	1. "no key (p) found",	"no key (y) found"
+
+#398. 4 ncsl lines
+	5,dh.c:166,169
+	5,dh.c:180,183
+	1 parameters
+ 	1. "no key (p) found",	"no key (g) found"
+
+#399. 4 ncsl lines
+	5,dh.c:166,169
+	5,dh.c:173,176
+	1 parameters
+ 	1. "no key (p) found",	"no key (q) found"
+
+#400. 4 ncsl lines
+	5,dh.c:99,102
+	5,dh.c:166,169
+	1 parameters
+ 	1. "no key (n) found",	"no key (p) found"
+
+#401. 4 ncsl lines
+	9,netssh.c:1316,1319
+	9,netssh.c:1333,1336
+	1 parameters
+ 	1. "pty-req",	"shell"
+
+#402. 4 ncsl lines
+	9,netssh.c:1333,1336
+	9,netssh.c:1341,1344
+	1 parameters
+ 	1. "shell",	"exec"
+
+#403. 3 ncsl lines
+	5,dh.c:502,504
+	5,dh.c:718,720
+	0 parameters
+
+#404. 16 ncsl lines
+	9,netssh.c:2730,2747
+	9,netssh.c:2741,2758
+	(overlap; region has 2 nonoverlapping matching 10-ncsl-line segments)
+	4 parameters
+ 	1. foundc2,	foundm1
+ 	2. nsccrypt,	ncsmac
+ 	3. foundm1,	foundm2
+ 	4. ncsmac,	nscmac
+
+#405. 9 ncsl lines
+	9,netssh.c:2720,2728
+	9,netssh.c:2741,2750
+	3 parameters
+ 	1. foundc1,	foundm1
+ 	2. ncscrypt,	ncsmac
+ 	3. cryptos,	macnames
+
+#406. 9 ncsl lines
+	9,netssh.c:2709,2718
+	9,netssh.c:2741,2750
+	3 parameters
+ 	1. foundpka,	foundm1
+ 	2. pkalg,	ncsmac
+ 	3. cryptos,	macnames
+
+#407. 7 ncsl lines
+	9,netssh.c:2698,2705
+	9,netssh.c:2741,2748
+	2 parameters
+ 	1. foundk,	foundm1
+ 	2. kexalg,	ncsmac
+
+#408. 6 ncsl lines
+	9,netssh.c:2730,2736
+	9,netssh.c:2752,2758
+	2 parameters
+ 	1. foundc2,	foundm2
+ 	2. nsccrypt,	nscmac
+
+#409. 6 ncsl lines
+	9,netssh.c:2720,2725
+	9,netssh.c:2752,2758
+	2 parameters
+ 	1. foundc1,	foundm2
+ 	2. ncscrypt,	nscmac
+
+#410. 6 ncsl lines
+	9,netssh.c:2709,2715
+	9,netssh.c:2752,2758
+	2 parameters
+ 	1. foundpka,	foundm2
+ 	2. pkalg,	nscmac
+
+#411. 6 ncsl lines
+	9,netssh.c:2698,2704
+	9,netssh.c:2752,2758
+	2 parameters
+ 	1. foundk,	foundm2
+ 	2. kexalg,	nscmac
+
+#412. 19 ncsl lines
+	9,netssh.c:2640,2660
+	9,netssh.c:2653,2673
+	(overlap; region has 2 nonoverlapping matching 12-ncsl-line segments)
+	7 parameters
+ 	1. foundc2,	foundm1
+ 	2. "s2c crypto algs not in cryptos",	"c2s mac algs not in cryptos"
+ 	3. nsccrypt,	ncsmac
+ 	4. "received C2S MAC algs: %s",	"received S2C MAC algs: %s"
+ 	5. foundm1,	foundm2
+ 	6. "c2s mac algs not in cryptos",	"s2c mac algs not in cryptos"
+ 	7. ncsmac,	nscmac
+
+#413. 10 ncsl lines
+	9,netssh.c:2601,2611
+	9,netssh.c:2653,2663
+	4 parameters
+ 	1. foundk,	foundm1
+ 	2. "kex algs not in kexes",	"c2s mac algs not in cryptos"
+ 	3. kexalg,	ncsmac
+ 	4. "received host key algs: %s",	"received S2C MAC algs: %s"
+
+#414. 3 ncsl lines
+	9,netssh.c:1856,1860
+	9,netssh.c:1965,1969
+	0 parameters
+
+#415. 3 ncsl lines
+	9,netssh.c:828,830
+	9,netssh.c:835,837
+	0 parameters
+
+#416. 5 ncsl lines
+	9,netssh.c:1311,1319
+	9,netssh.c:1340,1344
+	1 parameters
+ 	1. "pty-req",	"exec"
+
+#417. 5 ncsl lines
+	9,netssh.c:1802,1813
+	9,netssh.c:1830,1836
+	1 parameters
+ 	1. negotiate,	deferredinit
+
+#418. 3 ncsl lines
+	5,dh.c:575,578
+	6,dial.c:203,206
+	0 parameters
+
+#419. 3 ncsl lines
+	5,dh.c:436,439
+	6,dial.c:203,206
+	0 parameters
+
+#420. 3 ncsl lines
+	5,dh.c:789,792
+	6,dial.c:203,206
+	0 parameters
+
+#421. 6 ncsl lines
+	5,dh.c:550,556
+	5,dh.c:767,773
+	2 parameters
+ 	1. ret,	retval
+ 	2. dh_client11,	dh_client141
+
+#422. 3 ncsl lines
+	5,dh.c:617,620
+	6,dial.c:203,206
+	0 parameters
+
+#423. 3 ncsl lines
+	13,sshsession.c:439,442
+	14,transport.c:105,108
+	0 parameters
+
+#424. 3 ncsl lines
+	9,netssh.c:2680,2683
+	9,netssh.c:2920,2923
+	0 parameters
+
+#425. 3 ncsl lines
+	6,dial.c:202,204
+	9,netssh.c:2679,2681
+	0 parameters
+
+#426. 6 ncsl lines
+	9,netssh.c:935,940
+	9,netssh.c:1268,1273
+	1 parameters
+ 	1. "invalid connection",	"Invalid connection"
+
+#427. 6 ncsl lines
+	9,netssh.c:935,940
+	9,netssh.c:1078,1084
+	1 parameters
+ 	1. "invalid connection",	"bad connection"
+
+#428. 5 ncsl lines
+	9,netssh.c:741,745
+	9,netssh.c:935,939
+	0 parameters
+
+#429. 5 ncsl lines
+	9,netssh.c:609,613
+	9,netssh.c:1078,1083
+	0 parameters
+
+#430. 5 ncsl lines
+	9,netssh.c:609,613
+	9,netssh.c:1268,1272
+	0 parameters
+
+#431. 5 ncsl lines
+	9,netssh.c:609,613
+	9,netssh.c:741,745
+	0 parameters
+
+#432. 3 ncsl lines
+	9,netssh.c:1798,1800
+	10,pubkey.c:217,219
+	0 parameters
+
+#433. 3 ncsl lines
+	9,netssh.c:1798,1800
+	14,transport.c:207,209
+	0 parameters
+
+#434. 3 ncsl lines
+	9,netssh.c:1798,1800
+	10,pubkey.c:37,39
+	0 parameters
+
+#435. 6 ncsl lines
+	5,dh.c:19,24
+	5,dh.c:30,35
+	2 parameters
+ 	1. group1p,	group14p
+ 	2. "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381",	"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+
+#436. 5 ncsl lines
+	5,dh.c:98,102
+	5,dh.c:165,169
+	1 parameters
+ 	1. "no key (n) found",	"no key (p) found"
+
+#437. 5 ncsl lines
+	5,dh.c:107,111
+	5,dh.c:186,190
+	1 parameters
+ 	1. "no key (ek) found",	"no key (y) found"
+
+#438. 5 ncsl lines
+	5,dh.c:107,111
+	5,dh.c:179,183
+	1 parameters
+ 	1. "no key (ek) found",	"no key (g) found"
+
+#439. 5 ncsl lines
+	5,dh.c:107,111
+	5,dh.c:172,176
+	1 parameters
+ 	1. "no key (ek) found",	"no key (q) found"
+
+#440. 5 ncsl lines
+	5,dh.c:107,111
+	5,dh.c:165,169
+	1 parameters
+ 	1. "no key (ek) found",	"no key (p) found"
+
+#441. 4 ncsl lines
+	6,dial.c:226,229
+	7,dial.thread.c:328,331
+	0 parameters
+
+#442. 3 ncsl lines
+	13,sshsession.c:409,411
+	13,sshsession.c:438,440
+	0 parameters
+
+#443. 11 ncsl lines
+	9,netssh.c:2707,2718
+	9,netssh.c:2728,2739
+	4 parameters
+ 	1. pkas,	cryptos
+ 	2. foundpka,	foundc2
+ 	3. pkalg,	nsccrypt
+ 	4. cryptos,	macnames
+
+#444. 9 ncsl lines
+	9,netssh.c:2707,2716
+	9,netssh.c:2718,2726
+	3 parameters
+ 	1. pkas,	cryptos
+ 	2. foundpka,	foundc1
+ 	3. pkalg,	ncscrypt
+
+#445. 9 ncsl lines
+	9,netssh.c:2696,2705
+	9,netssh.c:2707,2716
+	3 parameters
+ 	1. kexes,	pkas
+ 	2. foundk,	foundpka
+ 	3. kexalg,	pkalg
+
+#446. 7 ncsl lines
+	9,netssh.c:1270,1276
+	9,netssh.c:1375,1381
+	0 parameters
+
+#447. 6 ncsl lines
+	9,netssh.c:361,366
+	9,netssh.c:937,942
+	1 parameters
+ 	1. "bad connection",	"invalid connection"
+
+#448. 4 ncsl lines
+	9,netssh.c:937,940
+	9,netssh.c:1375,1378
+	1 parameters
+ 	1. "invalid connection",	"Invalid connection"
+
+#449. 4 ncsl lines
+	9,netssh.c:361,364
+	9,netssh.c:1270,1273
+	1 parameters
+ 	1. "bad connection",	"Invalid connection"
+
+#450. 4 ncsl lines
+	9,netssh.c:361,364
+	9,netssh.c:1375,1378
+	1 parameters
+ 	1. "bad connection",	"Invalid connection"
+
+#451. 7 ncsl lines
+	9,netssh.c:493,499
+	9,netssh.c:1080,1087
+	1 parameters
+ 	1. "bad channel",	"bad connection"
+
+#452. 7 ncsl lines
+	9,netssh.c:887,893
+	9,netssh.c:1080,1087
+	0 parameters
+
+#453. 4 ncsl lines
+	9,netssh.c:1080,1084
+	9,netssh.c:1375,1378
+	1 parameters
+ 	1. "bad connection",	"Invalid connection"
+
+#454. 4 ncsl lines
+	9,netssh.c:361,364
+	9,netssh.c:1080,1084
+	0 parameters
+
+#455. 4 ncsl lines
+	9,netssh.c:887,890
+	9,netssh.c:937,940
+	1 parameters
+ 	1. "bad connection",	"invalid connection"
+
+#456. 4 ncsl lines
+	9,netssh.c:493,496
+	9,netssh.c:937,940
+	1 parameters
+ 	1. "bad channel",	"invalid connection"
+
+#457. 4 ncsl lines
+	9,netssh.c:887,890
+	9,netssh.c:1270,1273
+	1 parameters
+ 	1. "bad connection",	"Invalid connection"
+
+#458. 4 ncsl lines
+	9,netssh.c:493,496
+	9,netssh.c:1270,1273
+	1 parameters
+ 	1. "bad channel",	"Invalid connection"
+
+#459. 4 ncsl lines
+	9,netssh.c:361,364
+	9,netssh.c:887,890
+	0 parameters
+
+#460. 4 ncsl lines
+	9,netssh.c:361,364
+	9,netssh.c:493,496
+	1 parameters
+ 	1. "bad connection",	"bad channel"
+
+#461. 3 ncsl lines
+	9,netssh.c:361,363
+	9,netssh.c:743,745
+	0 parameters
+
+#462. 3 ncsl lines
+	9,netssh.c:361,363
+	9,netssh.c:611,613
+	0 parameters
+
+#463. 3 ncsl lines
+	9,netssh.c:743,745
+	9,netssh.c:887,889
+	0 parameters
+
+#464. 3 ncsl lines
+	9,netssh.c:493,495
+	9,netssh.c:743,745
+	0 parameters
+
+#465. 3 ncsl lines
+	9,netssh.c:743,745
+	9,netssh.c:1375,1377
+	0 parameters
+
+#466. 3 ncsl lines
+	9,netssh.c:611,613
+	9,netssh.c:887,889
+	0 parameters
+
+#467. 3 ncsl lines
+	9,netssh.c:493,495
+	9,netssh.c:611,613
+	0 parameters
+
+#468. 3 ncsl lines
+	9,netssh.c:611,613
+	9,netssh.c:1375,1377
+	0 parameters
+
+#469. 4 ncsl lines
+	5,dh.c:506,509
+	5,dh.c:723,726
+	0 parameters
+
+#470. 4 ncsl lines
+	5,dh.c:549,553
+	9,netssh.c:1687,1691
+	1 parameters
+ 	1. ret,	sc
+
+#471. 5 ncsl lines
+	9,netssh.c:2165,2169
+	9,netssh.c:2538,2542
+	1 parameters
+ 	1. SSH_MSG_SERVICE_REQUEST,	SSH_MSG_USERAUTH_SUCCESS
+
+#472. 3 ncsl lines
+	9,netssh.c:2155,2157
+	9,netssh.c:2538,2540
+	0 parameters
+
+#473. 3 ncsl lines
+	9,netssh.c:2205,2207
+	9,netssh.c:2538,2540
+	0 parameters
+
+#474. 3 ncsl lines
+	9,netssh.c:1205,1207
+	9,netssh.c:1478,1481
+	0 parameters
+
+#475. 3 ncsl lines
+	12,ssh.c:219,221
+	12,ssh.c:448,450
+	0 parameters
+
+#476. 3 ncsl lines
+	6,dial.c:191,195
+	13,sshsession.c:117,120
+	0 parameters
+
+#477. 3 ncsl lines
+	9,netssh.c:972,975
+	13,sshsession.c:117,120
+	0 parameters
+
+#478. 3 ncsl lines
+	6,dial.c:191,195
+	9,netssh.c:972,975
+	0 parameters
+
+#479. 3 ncsl lines
+	6,dial.c:191,195
+	7,dial.thread.c:158,161
+	0 parameters
+
+#480. 3 ncsl lines
+	7,dial.thread.c:158,161
+	9,netssh.c:972,975
+	0 parameters
+
+#481. 3 ncsl lines
+	12,ssh.c:449,452
+	13,sshsession.c:328,331
+	0 parameters
+
+#482. 3 ncsl lines
+	9,netssh.c:725,728
+	9,netssh.c:1495,1498
+	0 parameters
+
+#483. 3 ncsl lines
+	9,netssh.c:725,728
+	12,ssh.c:449,452
+	0 parameters
+
+#484. 3 ncsl lines
+	9,netssh.c:725,728
+	13,sshsession.c:328,331
+	0 parameters
+
+#485. 3 ncsl lines
+	9,netssh.c:1495,1498
+	12,ssh.c:449,452
+	0 parameters
+
+#486. 3 ncsl lines
+	9,netssh.c:2554,2557
+	12,ssh.c:449,452
+	0 parameters
+
+#487. 3 ncsl lines
+	6,dial.c:164,167
+	12,ssh.c:220,223
+	0 parameters
+
+#488. 3 ncsl lines
+	12,ssh.c:220,223
+	12,ssh.c:259,263
+	0 parameters
+
+#489. 3 ncsl lines
+	12,ssh.c:112,116
+	12,ssh.c:220,223
+	0 parameters
+
+#490. 3 ncsl lines
+	12,ssh.c:220,223
+	13,sshsession.c:302,305
+	0 parameters
+
+#491. 3 ncsl lines
+	7,dial.thread.c:149,152
+	12,ssh.c:220,223
+	0 parameters
+
+#492. 3 ncsl lines
+	9,netssh.c:381,384
+	12,ssh.c:220,223
+	0 parameters
+
+#493. 3 ncsl lines
+	7,dial.thread.c:149,152
+	9,netssh.c:381,384
+	0 parameters
+
+#494. 3 ncsl lines
+	9,netssh.c:381,384
+	13,sshsession.c:302,305
+	0 parameters
+
+#495. 3 ncsl lines
+	9,netssh.c:381,384
+	12,ssh.c:112,116
+	0 parameters
+
+#496. 3 ncsl lines
+	9,netssh.c:381,384
+	12,ssh.c:259,263
+	0 parameters
+
+#497. 3 ncsl lines
+	6,dial.c:164,167
+	9,netssh.c:381,384
+	0 parameters
+
+#498. 3 ncsl lines
+	9,netssh.c:793,795
+	9,netssh.c:1245,1247
+	0 parameters
+
+#499. 3 ncsl lines
+	9,netssh.c:793,795
+	12,ssh.c:309,311
+	0 parameters
+
+#500. 3 ncsl lines
+	9,netssh.c:793,795
+	9,netssh.c:2208,2210
+	0 parameters
+
+#501. 4 ncsl lines
+	13,sshsession.c:398,402
+	13,sshsession.c:411,415
+	1 parameters
+ 	1. get_string,	confine
+
+#502. 4 ncsl lines
+	6,dial.c:255,259
+	7,dial.thread.c:273,282
+	1 parameters
+ 	1. winner,	windfd
+
+#503. 4 ncsl lines
+	6,dial.c:193,197
+	6,dial.c:204,208
+	1 parameters
+ 	1. outstandingprocs,	reap
+
+#504. 4 ncsl lines
+	6,dial.c:193,197
+	7,dial.thread.c:275,284
+	1 parameters
+ 	1. outstandingprocs,	dialmulti
+
+#505. 19 ncsl lines
+	6,dial.c:257,278
+	7,dial.thread.c:159,180
+	0 parameters
+
+#506. 6 ncsl lines
+	9,netssh.c:123,129
+	9,netssh.c:139,145
+	1 parameters
+ 	1. sshlog,	sshdebug
+
+#507. 4 ncsl lines
+	9,netssh.c:392,396
+	9,netssh.c:726,730
+	1 parameters
+ 	1. stlisconn,	readreqrem
+
+#508. 4 ncsl lines
+	9,netssh.c:392,396
+	9,netssh.c:2555,2559
+	1 parameters
+ 	1. stlisconn,	reader
+
+#509. 4 ncsl lines
+	9,netssh.c:924,928
+	9,netssh.c:1496,1500
+	1 parameters
+ 	1. stwrite,	stflush
+
+#510. 4 ncsl lines
+	9,netssh.c:597,601
+	9,netssh.c:1496,1500
+	1 parameters
+ 	1. stread,	stflush
+
+#511. 4 ncsl lines
+	9,netssh.c:313,317
+	9,netssh.c:1496,1500
+	1 parameters
+ 	1. stopen,	stflush
+
+#512. 4 ncsl lines
+	14,transport.c:22,26
+	14,transport.c:214,218
+	1 parameters
+ 	1. init_packet,	dump_packet
+
+#513. 4 ncsl lines
+	11,rsa2ssh2.c:20,24
+	12,ssh.c:450,454
+	0 parameters
+
+#514. 4 ncsl lines
+	9,netssh.c:164,168
+	12,ssh.c:450,454
+	1 parameters
+ 	1. threadmain,	main
+
+#515. 10 ncsl lines
+	9,netssh.c:213,224
+	9,netssh.c:222,233
+	(overlap; region has 2 nonoverlapping matching 8-ncsl-line segments)
+	3 parameters
+ 	1. readio,	writeio
+ 	2. ioread,	iowrite
+ 	3. read,	write
+
+#516. 11 ncsl lines
+	9,netssh.c:2582,2595
+	9,netssh.c:2681,2693
+	1 parameters
+ 	1. validatekexs,	validatekexc
+
+#517. 4 ncsl lines
+	12,ssh.c:30,38
+	12,ssh.c:148,152
+	1 parameters
+ 	1. shutdown,	starttunnel
+
+#518. 4 ncsl lines
+	12,ssh.c:80,84
+	12,ssh.c:148,152
+	1 parameters
+ 	1. parseargs,	starttunnel
+
+#519. 4 ncsl lines
+	12,ssh.c:260,265
+	12,ssh.c:314,322
+	1 parameters
+ 	1. keyproc,	bidircopy
+
+#520. 9 ncsl lines
+	6,dial.c:156,165
+	7,dial.thread.c:150,159
+	0 parameters
+
+#521. 6 ncsl lines
+	3,cipherrc4.c:25,31
+	3,cipherrc4.c:31,37
+	1 parameters
+ 	1. encryptrc4,	decryptrc4
+
+#522. 10 ncsl lines
+	1,cipheraes.c:47,57
+	1,cipheraes.c:57,67
+	2 parameters
+ 	1. encryptaes,	decryptaes
+ 	2. aesCBCencrypt,	aesCBCdecrypt
+
+#523. 4 ncsl lines
+	1,cipheraes.c:57,61
+	3,cipherrc4.c:25,29
+	1 parameters
+ 	1. decryptaes,	encryptrc4
+
+#524. 4 ncsl lines
+	1,cipheraes.c:47,51
+	3,cipherrc4.c:25,29
+	1 parameters
+ 	1. encryptaes,	encryptrc4
+
+#525. 4 ncsl lines
+	1,cipheraes.c:57,61
+	3,cipherrc4.c:31,35
+	1 parameters
+ 	1. decryptaes,	decryptrc4
+
+#526. 4 ncsl lines
+	1,cipheraes.c:47,51
+	3,cipherrc4.c:31,35
+	1 parameters
+ 	1. encryptaes,	decryptrc4
+
+#527. 4 ncsl lines
+	2,cipherblowfish.c:49,53
+	3,cipherrc4.c:31,35
+	1 parameters
+ 	1. decryptblowfish,	decryptrc4
+
+#528. 4 ncsl lines
+	2,cipherblowfish.c:49,53
+	3,cipherrc4.c:25,29
+	1 parameters
+ 	1. decryptblowfish,	encryptrc4
+
+#529. 6 ncsl lines
+	0,cipher3des.c:29,35
+	0,cipher3des.c:35,41
+	2 parameters
+ 	1. encrypt3des,	decrypt3des
+ 	2. des3CBCencrypt,	des3CBCdecrypt
+
+#530. 6 ncsl lines
+	0,cipher3des.c:35,41
+	2,cipherblowfish.c:43,49
+	2 parameters
+ 	1. decrypt3des,	encryptblowfish
+ 	2. des3CBCdecrypt,	bfCBCencrypt
+
+#531. 4 ncsl lines
+	0,cipher3des.c:29,33
+	3,cipherrc4.c:31,35
+	1 parameters
+ 	1. encrypt3des,	decryptrc4
+
+#532. 4 ncsl lines
+	2,cipherblowfish.c:43,47
+	3,cipherrc4.c:31,35
+	1 parameters
+ 	1. encryptblowfish,	decryptrc4
+
+#533. 4 ncsl lines
+	0,cipher3des.c:29,33
+	2,cipherblowfish.c:49,53
+	1 parameters
+ 	1. encrypt3des,	decryptblowfish
+
+#534. 4 ncsl lines
+	2,cipherblowfish.c:43,47
+	2,cipherblowfish.c:49,53
+	1 parameters
+ 	1. encryptblowfish,	decryptblowfish
+
+#535. 4 ncsl lines
+	0,cipher3des.c:29,33
+	1,cipheraes.c:57,61
+	1 parameters
+ 	1. encrypt3des,	decryptaes
+
+#536. 4 ncsl lines
+	1,cipheraes.c:57,61
+	2,cipherblowfish.c:43,47
+	1 parameters
+ 	1. decryptaes,	encryptblowfish
+
+#537. 4 ncsl lines
+	0,cipher3des.c:29,33
+	1,cipheraes.c:47,51
+	1 parameters
+ 	1. encrypt3des,	encryptaes
+
+#538. 4 ncsl lines
+	1,cipheraes.c:47,51
+	2,cipherblowfish.c:43,47
+	1 parameters
+ 	1. encryptaes,	encryptblowfish
+
+#539. 4 ncsl lines
+	0,cipher3des.c:35,39
+	3,cipherrc4.c:25,29
+	1 parameters
+ 	1. decrypt3des,	encryptrc4
+
+#540. 4 ncsl lines
+	0,cipher3des.c:35,39
+	3,cipherrc4.c:31,35
+	1 parameters
+ 	1. decrypt3des,	decryptrc4
+
+#541. 3 ncsl lines
+	9,netssh.c:630,632
+	9,netssh.c:754,756
+	0 parameters
+
+#542. 4 ncsl lines
+	9,netssh.c:645,648
+	9,netssh.c:688,691
+	1 parameters
+ 	1. Qclone,	Qreqrem
+
+#543. 4 ncsl lines
+	9,netssh.c:645,648
+	9,netssh.c:707,710
+	1 parameters
+ 	1. Qclone,	Qtcp
+
+#544. 4 ncsl lines
+	9,netssh.c:688,691
+	9,netssh.c:707,710
+	1 parameters
+ 	1. Qreqrem,	Qtcp
+
+#545. 3 ncsl lines
+	9,netssh.c:645,647
+	9,netssh.c:719,721
+	0 parameters
+
+#546. 3 ncsl lines
+	9,netssh.c:707,709
+	9,netssh.c:719,721
+	0 parameters
+
+#547. 3 ncsl lines
+	9,netssh.c:378,380
+	9,netssh.c:688,690
+	0 parameters
+
+#548. 3 ncsl lines
+	9,netssh.c:378,380
+	9,netssh.c:719,721
+	0 parameters
+
+#549. 3 ncsl lines
+	9,netssh.c:378,380
+	9,netssh.c:645,647
+	0 parameters
+
+#550. 4 ncsl lines
+	9,netssh.c:796,799
+	9,netssh.c:867,870
+	0 parameters
+
+#551. 3 ncsl lines
+	9,netssh.c:594,596
+	9,netssh.c:796,798
+	0 parameters
+
+#552. 4 ncsl lines
+	9,netssh.c:2424,2428
+	13,sshsession.c:146,150
+	0 parameters
+
+#553. 4 ncsl lines
+	9,netssh.c:2210,2214
+	13,sshsession.c:146,150
+	0 parameters
+
+#554. 4 ncsl lines
+	0,cipher3des.c:27,31
+	9,netssh.c:2210,2214
+	1 parameters
+ 	1. cs,	0
+
+#555. 4 ncsl lines
+	0,cipher3des.c:27,31
+	9,netssh.c:2424,2428
+	1 parameters
+ 	1. cs,	0
+
+#556. 4 ncsl lines
+	6,dial.c:522,529
+	9,netssh.c:2210,2214
+	1 parameters
+ 	1. sl,	0
+
+#557. 4 ncsl lines
+	7,dial.thread.c:516,523
+	9,netssh.c:2210,2214
+	1 parameters
+ 	1. sl,	0
+
+#558. 4 ncsl lines
+	6,dial.c:522,529
+	9,netssh.c:2424,2428
+	1 parameters
+ 	1. sl,	0
+
+#559. 4 ncsl lines
+	7,dial.thread.c:516,523
+	9,netssh.c:2424,2428
+	1 parameters
+ 	1. sl,	0
+
+#560. 7 ncsl lines
+	5,dh.c:90,96
+	5,dh.c:157,163
+	0 parameters
+
+#561. 16 ncsl lines
+	6,dial.c:14,44
+	7,dial.thread.c:24,49
+	1 parameters
+ 	1. pid,	cfd
+
+#562. 6 ncsl lines
+	9,netssh.c:721,726
+	9,netssh.c:968,973
+	0 parameters
+
+#563. 3 ncsl lines
+	12,ssh.c:496,498
+	13,sshsession.c:215,217
+	0 parameters
+
+#564. 3 ncsl lines
+	9,netssh.c:190,192
+	12,ssh.c:496,498
+	0 parameters
+
+#565. 5 ncsl lines
+	9,netssh.c:2359,2363
+	9,netssh.c:2368,2372
+	1 parameters
+ 	1. SSH_MSG_CHANNEL_EOF,	SSH_MSG_CHANNEL_CLOSE
+
+#566. 4 ncsl lines
+	9,netssh.c:2311,2314
+	9,netssh.c:2368,2371
+	1 parameters
+ 	1. SSH_MSG_CHANNEL_OPEN_CONFIRMATION,	SSH_MSG_CHANNEL_CLOSE
+
+#567. 4 ncsl lines
+	9,netssh.c:2320,2323
+	9,netssh.c:2368,2371
+	1 parameters
+ 	1. SSH_MSG_CHANNEL_OPEN_FAILURE,	SSH_MSG_CHANNEL_CLOSE
+
+#568. 4 ncsl lines
+	9,netssh.c:2311,2314
+	9,netssh.c:2359,2362
+	1 parameters
+ 	1. SSH_MSG_CHANNEL_OPEN_CONFIRMATION,	SSH_MSG_CHANNEL_EOF
+
+#569. 4 ncsl lines
+	9,netssh.c:2320,2323
+	9,netssh.c:2359,2362
+	1 parameters
+ 	1. SSH_MSG_CHANNEL_OPEN_FAILURE,	SSH_MSG_CHANNEL_EOF
+
+#570. 4 ncsl lines
+	9,netssh.c:2311,2314
+	9,netssh.c:2397,2400
+	1 parameters
+ 	1. SSH_MSG_CHANNEL_OPEN_CONFIRMATION,	SSH_MSG_CHANNEL_REQUEST
+
+#571. 4 ncsl lines
+	9,netssh.c:2320,2323
+	9,netssh.c:2397,2400
+	1 parameters
+ 	1. SSH_MSG_CHANNEL_OPEN_FAILURE,	SSH_MSG_CHANNEL_REQUEST
+
+#572. 4 ncsl lines
+	9,netssh.c:2368,2371
+	9,netssh.c:2397,2400
+	1 parameters
+ 	1. SSH_MSG_CHANNEL_CLOSE,	SSH_MSG_CHANNEL_REQUEST
+
+#573. 4 ncsl lines
+	9,netssh.c:690,693
+	9,netssh.c:965,968
+	1 parameters
+ 	1. readreqrem,	writereqremproc
+
+#574. 3 ncsl lines
+	9,netssh.c:380,382
+	9,netssh.c:794,796
+	0 parameters
+
+#575. 3 ncsl lines
+	9,netssh.c:724,726
+	9,netssh.c:794,796
+	0 parameters
+
+#576. 3 ncsl lines
+	9,netssh.c:794,796
+	9,netssh.c:971,973
+	0 parameters
+
+#577. 3 ncsl lines
+	9,netssh.c:794,796
+	9,netssh.c:961,963
+	0 parameters
+
+#578. 3 ncsl lines
+	9,netssh.c:794,796
+	12,ssh.c:218,220
+	0 parameters
+
+#579. 3 ncsl lines
+	9,netssh.c:794,796
+	9,netssh.c:1701,1703
+	0 parameters
+
+#580. 3 ncsl lines
+	9,netssh.c:666,668
+	9,netssh.c:794,796
+	0 parameters
+
+#581. 4 ncsl lines
+	9,netssh.c:870,873
+	12,ssh.c:369,373
+	1 parameters
+ 	1. threadexits,	bail
+
+#582. 5 ncsl lines
+	9,netssh.c:2209,2214
+	9,netssh.c:2423,2428
+	0 parameters
+
+#583. 30 ncsl lines
+	6,dial.c:364,408
+	7,dial.thread.c:367,411
+	1 parameters
+ 	1. rv,	dfd
+
+#584. 4 ncsl lines
+	5,dh.c:574,578
+	5,dh.c:616,620
+	1 parameters
+ 	1. p,	r
+
+#585. 6 ncsl lines
+	9,netssh.c:608,613
+	9,netssh.c:934,939
+	1 parameters
+ 	1. "stread",	"stwrite"
+
+#586. 5 ncsl lines
+	9,netssh.c:2629,2634
+	9,netssh.c:2721,2725
+	0 parameters
+
+#587. 5 ncsl lines
+	9,netssh.c:2616,2621
+	9,netssh.c:2710,2715
+	0 parameters
+
+#588. 5 ncsl lines
+	9,netssh.c:2642,2647
+	9,netssh.c:2731,2736
+	0 parameters
+
+#589. 5 ncsl lines
+	9,netssh.c:2655,2660
+	9,netssh.c:2742,2747
+	0 parameters
+
+#590. 5 ncsl lines
+	9,netssh.c:2603,2608
+	9,netssh.c:2699,2704
+	0 parameters
+
+#591. 9 ncsl lines
+	9,netssh.c:2668,2677
+	9,netssh.c:2753,2762
+	0 parameters
+
+#592. 3 ncsl lines
+	9,netssh.c:1814,1817
+	9,netssh.c:1978,1981
+	0 parameters
+
+#593. 8 ncsl lines
+	9,netssh.c:1076,1084
+	9,netssh.c:1266,1273
+	2 parameters
+ 	1. "writectlproc",	"writereqremproc"
+ 	2. "bad connection",	"Invalid connection"
+
+#594. 7 ncsl lines
+	9,netssh.c:739,745
+	9,netssh.c:1266,1272
+	1 parameters
+ 	1. "readreqrem",	"writereqremproc"
+
+#595. 6 ncsl lines
+	9,netssh.c:405,411
+	9,netssh.c:1372,1377
+	2 parameters
+ 	1. "stlisconn",	"writedataproc"
+ 	2. cl,	c
+
+#596. 6 ncsl lines
+	9,netssh.c:405,411
+	9,netssh.c:884,889
+	2 parameters
+ 	1. "stlisconn",	"readdata"
+ 	2. cl,	c
+
+#597. 6 ncsl lines
+	9,netssh.c:405,411
+	9,netssh.c:490,495
+	2 parameters
+ 	1. "stlisconn",	"stlischan"
+ 	2. cl,	c
+
+#598. 6 ncsl lines
+	9,netssh.c:122,128
+	9,netssh.c:138,144
+	2 parameters
+ 	1. id,	p
+ 	2. sshlog,	sshdebug
+
+#599. 5 ncsl lines
+	9,netssh.c:312,317
+	9,netssh.c:923,928
+	1 parameters
+ 	1. stopen,	stwrite
+
+#600. 5 ncsl lines
+	9,netssh.c:391,396
+	9,netssh.c:872,877
+	1 parameters
+ 	1. stlisconn,	readdata
+
+#601. 3 ncsl lines
+	9,netssh.c:872,875
+	9,netssh.c:923,926
+	0 parameters
+
+#602. 3 ncsl lines
+	9,netssh.c:312,315
+	9,netssh.c:872,875
+	0 parameters
+
+#603. 3 ncsl lines
+	9,netssh.c:551,554
+	9,netssh.c:872,875
+	0 parameters
+
+#604. 3 ncsl lines
+	9,netssh.c:476,479
+	9,netssh.c:551,554
+	0 parameters
+
+#605. 3 ncsl lines
+	9,netssh.c:551,554
+	9,netssh.c:1252,1255
+	0 parameters
+
+#606. 3 ncsl lines
+	9,netssh.c:391,394
+	9,netssh.c:551,554
+	0 parameters
+
+#607. 3 ncsl lines
+	9,netssh.c:551,554
+	9,netssh.c:923,926
+	0 parameters
+
+#608. 3 ncsl lines
+	9,netssh.c:312,315
+	9,netssh.c:551,554
+	0 parameters
+
+#609. 4 ncsl lines
+	9,netssh.c:311,315
+	9,netssh.c:1251,1255
+	1 parameters
+ 	1. freeme,	buf
+
+#610. 3 ncsl lines
+	9,netssh.c:1047,1049
+	9,netssh.c:1251,1253
+	0 parameters
+
+#611. 3 ncsl lines
+	9,netssh.c:2148,2150
+	9,netssh.c:2431,2433
+	0 parameters
+
+#612. 3 ncsl lines
+	9,netssh.c:1145,1147
+	9,netssh.c:1492,1494
+	0 parameters
+
+#613. 3 ncsl lines
+	9,netssh.c:1145,1147
+	9,netssh.c:1446,1448
+	0 parameters
+
+#614. 3 ncsl lines
+	9,netssh.c:1145,1147
+	9,netssh.c:1236,1238
+	0 parameters
+
+#615. 3 ncsl lines
+	9,netssh.c:1145,1147
+	9,netssh.c:1186,1188
+	0 parameters
+
+#616. 5 ncsl lines
+	12,ssh.c:71,75
+	12,ssh.c:388,392
+	1 parameters
+ 	1. fd,	nfd
+

+ 2638 - 0
sys/src/cmd/ssh2/dup.pro

@@ -0,0 +1,2638 @@
+filenum,filename:interval_start-interval_end, # distinct matches = matchnum:
+       list of matches represented as match_k.i_k
+where i_k is 1 for first interval of match_k and 2 for the second
+
+0,cipher3des.c:1,6, # distinct matches = 6:
+	117.1, 134.1, 125.1, 120.1, 129.1, 119.1
+
+0,cipher3des.c:7,7, # distinct matches = 5:
+	117.1, 125.1, 120.1, 129.1, 119.1
+
+0,cipher3des.c:8,9, # distinct matches = 3:
+	117.1, 120.1, 119.1
+
+0,cipher3des.c:10,14, # distinct matches = 3:
+	313.1, 117.1, 119.1
+
+0,cipher3des.c:15,16, # distinct matches = 2:
+	117.1, 119.1
+
+0,cipher3des.c:17,17, # distinct matches = 1:
+	117.1
+
+
+0,cipher3des.c:27,27, # distinct matches = 5:
+	555.1, 344.1, 347.1, 348.1, 554.1
+
+0,cipher3des.c:28,28, # distinct matches = 7:
+	89.1, 88.1, 555.1, 344.1, 347.1, 348.1, 554.1
+
+0,cipher3des.c:29,31, # distinct matches = 12:
+	531.1, 533.1, 535.1, 537.1, 529.1, 89.1, 88.1, 555.1, 344.1, 347.1, 348.1, 554.1
+
+0,cipher3des.c:32,33, # distinct matches = 7:
+	531.1, 533.1, 535.1, 537.1, 529.1, 89.1, 88.1
+
+0,cipher3des.c:34,34, # distinct matches = 2:
+	529.1, 88.1
+
+0,cipher3des.c:35,35, # distinct matches = 8:
+	539.1, 529.2, 540.1, 340.1, 530.1, 341.1, 529.1, 88.1
+
+0,cipher3des.c:36,39, # distinct matches = 7:
+	539.1, 529.2, 540.1, 340.1, 530.1, 341.1, 88.1
+
+0,cipher3des.c:40,41, # distinct matches = 2:
+	529.2, 530.1
+
+
+1,cipheraes.c:1,6, # distinct matches = 6:
+	128.1, 124.1, 122.1, 135.1, 121.1, 120.2
+
+1,cipheraes.c:7,7, # distinct matches = 5:
+	128.1, 124.1, 122.1, 121.1, 120.2
+
+1,cipheraes.c:8,8, # distinct matches = 3:
+	122.1, 121.1, 120.2
+
+
+1,cipheraes.c:12,16, # distinct matches = 3:
+	314.1, 313.2, 312.1
+
+
+1,cipheraes.c:29,34, # distinct matches = 2:
+	328.1, 327.1
+
+1,cipheraes.c:35,35, # distinct matches = 3:
+	327.2, 328.1, 327.1
+
+1,cipheraes.c:36,40, # distinct matches = 2:
+	327.2, 327.1
+
+1,cipheraes.c:41,41, # distinct matches = 3:
+	328.2, 327.2, 327.1
+
+1,cipheraes.c:42,46, # distinct matches = 2:
+	328.2, 327.2
+
+1,cipheraes.c:47,47, # distinct matches = 9:
+	537.2, 538.1, 524.1, 526.1, 339.1, 522.1, 341.2, 241.1, 328.2, 327.2
+
+1,cipheraes.c:48,51, # distinct matches = 7:
+	537.2, 538.1, 524.1, 526.1, 339.1, 522.1, 341.2, 241.1
+
+1,cipheraes.c:52,53, # distinct matches = 1:
+	522.1, 241.1
+
+
+1,cipheraes.c:57,57, # distinct matches = 7:
+	338.1, 522.2 (coincides with 241.2), 523.1, 525.1, 535.2, 536.1, 241.2, 340.2, 522.1
+
+1,cipheraes.c:58,61, # distinct matches = 7:
+	338.1, 522.2 (coincides with 241.2), 523.1, 525.1, 535.2, 536.1, 241.2, 340.2
+
+1,cipheraes.c:62,63, # distinct matches = 1:
+	522.2 (coincides with 241.2), 241.2
+
+
+1,cipheraes.c:69,76, # distinct matches = 1:
+	143.1
+
+1,cipheraes.c:77,83, # distinct matches = 2:
+	143.2, 143.1
+
+1,cipheraes.c:84,91, # distinct matches = 1:
+	143.2
+
+2,cipherblowfish.c:1,6, # distinct matches = 6:
+	119.2, 118.1, 131.1, 127.1, 122.2, 132.1
+
+2,cipherblowfish.c:7,7, # distinct matches = 5:
+	119.2, 118.1, 131.1, 127.1, 122.2
+
+2,cipherblowfish.c:8,9, # distinct matches = 3:
+	119.2, 118.1, 122.2
+
+2,cipherblowfish.c:10,14, # distinct matches = 3:
+	312.2, 119.2, 118.1
+
+2,cipherblowfish.c:15,16, # distinct matches = 2:
+	119.2, 118.1
+
+
+2,cipherblowfish.c:42,42, # distinct matches = 2:
+	168.1, 88.2
+
+2,cipherblowfish.c:43,47, # distinct matches = 7:
+	538.2, 536.2, 534.1, 532.1, 530.2, 168.1, 88.2
+
+2,cipherblowfish.c:48,48, # distinct matches = 2:
+	530.2, 88.2
+
+2,cipherblowfish.c:49,49, # distinct matches = 8:
+	528.1, 527.1, 533.2, 339.2, 534.2, 338.2, 530.2, 88.2
+
+2,cipherblowfish.c:50,53, # distinct matches = 7:
+	528.1, 527.1, 533.2, 339.2, 534.2, 338.2, 88.2
+
+
+3,cipherrc4.c:1,6, # distinct matches = 6:
+	130.1, 121.2, 133.1, 126.1, 117.2, 118.2
+
+3,cipherrc4.c:7,7, # distinct matches = 5:
+	130.1, 121.2, 126.1, 117.2, 118.2
+
+3,cipherrc4.c:8,9, # distinct matches = 3:
+	121.2, 117.2, 118.2
+
+3,cipherrc4.c:10,14, # distinct matches = 3:
+	314.2, 117.2, 118.2
+
+3,cipherrc4.c:15,16, # distinct matches = 2:
+	117.2, 118.2
+
+3,cipherrc4.c:17,17, # distinct matches = 1:
+	117.2
+
+
+3,cipherrc4.c:24,24, # distinct matches = 2:
+	168.2, 89.2
+
+3,cipherrc4.c:25,29, # distinct matches = 7:
+	523.2, 539.2, 521.1, 524.2, 528.2, 168.2, 89.2
+
+3,cipherrc4.c:30,30, # distinct matches = 1:
+	521.1
+
+3,cipherrc4.c:31,31, # distinct matches = 8:
+	540.2, 525.2, 526.2, 531.2, 521.2, 527.2, 532.2, 521.1
+
+3,cipherrc4.c:32,35, # distinct matches = 7:
+	540.2, 525.2, 526.2, 531.2, 521.2, 527.2, 532.2
+
+3,cipherrc4.c:36,37, # distinct matches = 1:
+	521.2
+
+
+5,dh.c:19,24, # distinct matches = 1:
+	435.1
+
+
+5,dh.c:30,35, # distinct matches = 1:
+	435.2
+
+
+5,dh.c:71,79, # distinct matches = 1:
+	163.1
+
+
+5,dh.c:81,88, # distinct matches = 1:
+	61.1
+
+
+5,dh.c:90,96, # distinct matches = 1:
+	560.1
+
+5,dh.c:97,97, # distinct matches = 1:
+	142.1
+
+5,dh.c:98,98, # distinct matches = 2:
+	436.1, 142.1
+
+5,dh.c:99,99, # distinct matches = 4:
+	56.1, 400.1, 323.1, 436.1, 142.1
+
+5,dh.c:100,102, # distinct matches = 5:
+	197.1, 56.1, 400.1, 323.1, 436.1, 142.1
+
+
+5,dh.c:106,106, # distinct matches = 1:
+	142.2
+
+5,dh.c:107,107, # distinct matches = 5:
+	438.1, 439.1, 440.1, 437.1, 142.2
+
+5,dh.c:108,108, # distinct matches = 7:
+	319.1, 59.1, 438.1, 439.1, 440.1, 437.1, 142.2
+
+5,dh.c:109,111, # distinct matches = 8:
+	191.1, 319.1, 59.1, 438.1, 439.1, 440.1, 437.1, 142.2
+
+
+5,dh.c:122,124, # distinct matches = 8:
+	193.1, 194.1, 195.1, 196.1, 197.2, 198.1, 191.2, 192.1
+
+
+5,dh.c:137,145, # distinct matches = 1:
+	163.2
+
+
+5,dh.c:147,153, # distinct matches = 1:
+	61.2
+
+5,dh.c:154,154, # distinct matches = 8:
+	59.2, 53.1, 57.1, 56.2, 55.1, 54.1, 58.1, 61.2
+
+5,dh.c:155,156, # distinct matches = 8:
+	193.2, 59.2, 53.1, 57.1, 56.2, 55.1, 54.1, 58.1
+
+5,dh.c:157,157, # distinct matches = 9:
+	560.2, 193.2, 59.2, 53.1, 57.1, 56.2, 55.1, 54.1, 58.1
+
+5,dh.c:158,163, # distinct matches = 1:
+	560.2
+
+5,dh.c:164,164, # distinct matches = 3:
+	139.1, 141.1, 140.1
+
+5,dh.c:165,165, # distinct matches = 5:
+	436.2, 440.2, 139.1, 141.1, 140.1
+
+5,dh.c:166,166, # distinct matches = 7:
+	400.2, 57.2, 324.1, 397.1, 398.1, 399.1, 436.2 (coincides with 400.2), 440.2, 139.1, 141.1, 140.1
+
+5,dh.c:167,169, # distinct matches = 8:
+	198.2, 400.2, 57.2, 324.1, 397.1, 398.1, 399.1, 436.2 (coincides with 400.2), 440.2, 139.1, 141.1, 140.1
+
+
+5,dh.c:171,171, # distinct matches = 3:
+	172.1, 140.2, 171.1
+
+5,dh.c:172,172, # distinct matches = 4:
+	439.2, 172.1, 140.2, 171.1
+
+5,dh.c:173,173, # distinct matches = 6:
+	55.2, 322.1, 399.2, 439.2, 172.1, 140.2 (coincides with 399.2), 171.1
+
+5,dh.c:174,176, # distinct matches = 7:
+	196.2, 55.2, 322.1, 399.2, 439.2, 172.1, 140.2 (coincides with 399.2), 171.1
+
+
+5,dh.c:178,178, # distinct matches = 2:
+	171.2, 139.2
+
+5,dh.c:179,179, # distinct matches = 3:
+	438.2, 171.2, 139.2
+
+5,dh.c:180,180, # distinct matches = 5:
+	321.1, 54.2, 398.2, 438.2, 171.2, 139.2 (coincides with 398.2)
+
+5,dh.c:181,183, # distinct matches = 6:
+	195.2, 321.1, 54.2, 398.2, 438.2, 171.2, 139.2 (coincides with 398.2)
+
+
+5,dh.c:185,185, # distinct matches = 2:
+	141.2, 172.2
+
+5,dh.c:186,186, # distinct matches = 3:
+	437.2, 141.2, 172.2
+
+5,dh.c:187,187, # distinct matches = 5:
+	53.2, 320.1, 397.2, 437.2, 141.2 (coincides with 397.2), 172.2
+
+5,dh.c:188,190, # distinct matches = 6:
+	194.2, 53.2, 320.1, 397.2, 437.2, 141.2 (coincides with 397.2), 172.2
+
+
+5,dh.c:198,198, # distinct matches = 7:
+	323.2, 320.2, 319.2, 58.2, 321.2, 322.2, 324.2
+
+5,dh.c:199,201, # distinct matches = 8:
+	192.2, 323.2, 320.2, 319.2, 58.2, 321.2, 322.2, 324.2
+
+
+5,dh.c:204,209, # distinct matches = 1:
+	330.1
+
+
+5,dh.c:243,248, # distinct matches = 1:
+	329.1
+
+
+5,dh.c:259,263, # distinct matches = 1:
+	30.1
+
+
+5,dh.c:280,282, # distinct matches = 1:
+	293.1, 52.1
+
+5,dh.c:283,284, # distinct matches = 1:
+	52.1
+
+
+5,dh.c:289,291, # distinct matches = 1:
+	199.1
+
+
+5,dh.c:296,304, # distinct matches = 1:
+	309.1
+
+
+5,dh.c:320,324, # distinct matches = 1:
+	30.2
+
+
+5,dh.c:371,376, # distinct matches = 1:
+	330.2
+
+
+5,dh.c:387,392, # distinct matches = 1:
+	329.2
+
+
+5,dh.c:418,420, # distinct matches = 1:
+	52.2, 293.2 (coincides with 52.2)
+
+5,dh.c:421,422, # distinct matches = 1:
+	52.2
+
+
+5,dh.c:424,426, # distinct matches = 1:
+	199.2
+
+
+5,dh.c:428,431, # distinct matches = 1:
+	309.2
+
+
+5,dh.c:436,439, # distinct matches = 5:
+	219.1, 303.1, 419.1, 167.1, 306.1
+
+
+5,dh.c:487,492, # distinct matches = 1:
+	325.1
+
+
+5,dh.c:502,505, # distinct matches = 1:
+	403.1
+
+5,dh.c:506,509, # distinct matches = 1:
+	469.1
+
+
+5,dh.c:549,549, # distinct matches = 1:
+	470.1
+
+5,dh.c:550,553, # distinct matches = 2:
+	421.1, 470.1
+
+5,dh.c:554,556, # distinct matches = 1:
+	421.1
+
+
+5,dh.c:572,573, # distinct matches = 5:
+	369.1, 374.1, 373.1, 368.1, 375.1
+
+5,dh.c:574,574, # distinct matches = 6:
+	584.1, 369.1, 374.1, 373.1, 368.1, 375.1
+
+5,dh.c:575,578, # distinct matches = 4:
+	304.1, 418.1, 305.1, 584.1
+
+
+5,dh.c:616,616, # distinct matches = 1:
+	584.2
+
+5,dh.c:617,620, # distinct matches = 5:
+	303.2, 301.1, 300.1, 422.1, 584.2
+
+
+5,dh.c:707,712, # distinct matches = 1:
+	325.2
+
+
+5,dh.c:718,720, # distinct matches = 1:
+	403.2
+
+
+5,dh.c:723,726, # distinct matches = 1:
+	469.2
+
+
+5,dh.c:767,773, # distinct matches = 1:
+	421.2
+
+
+5,dh.c:784,786, # distinct matches = 4:
+	378.1, 375.2, 377.1, 376.1
+
+
+5,dh.c:789,792, # distinct matches = 4:
+	420.1, 308.1, 307.1, 306.2
+
+
+5,dh.c:795,798, # distinct matches = 6:
+	82.1, 81.1, 83.1, 84.1, 80.1, 79.1
+
+
+5,dh.c:826,834, # distinct matches = 5:
+	115.1, 114.1, 113.1, 112.1, 116.1
+
+5,dh.c:835,843, # distinct matches = 5:
+	112.2, 115.1, 114.1, 113.1, 112.1
+
+5,dh.c:844,852, # distinct matches = 5:
+	113.2, 112.2, 114.1, 113.1, 112.1
+
+5,dh.c:853,861, # distinct matches = 5:
+	114.2, 113.2, 112.2, 113.1, 112.1
+
+5,dh.c:862,870, # distinct matches = 5:
+	115.2, 114.2, 113.2, 112.2, 112.1
+
+5,dh.c:871,877, # distinct matches = 5:
+	116.2, 115.2, 114.2, 113.2, 112.2
+
+
+6,dial.c:4,6, # distinct matches = 1:
+	138.1
+
+
+6,dial.c:14,44, # distinct matches = 1:
+	561.1
+
+
+6,dial.c:61,65, # distinct matches = 1:
+	362.1
+
+
+6,dial.c:68,89, # distinct matches = 1:
+	257.1
+
+6,dial.c:90,93, # distinct matches = 3:
+	98.1, 100.1, 257.1
+
+6,dial.c:94,96, # distinct matches = 1:
+	257.1
+
+6,dial.c:97,99, # distinct matches = 2:
+	34.1, 257.1
+
+6,dial.c:100,103, # distinct matches = 3:
+	99.1, 98.2, 257.1
+
+6,dial.c:104,124, # distinct matches = 1:
+	257.1
+
+
+6,dial.c:141,141, # distinct matches = 6:
+	220.1, 74.1, 87.1, 84.2, 76.1, 221.1
+
+6,dial.c:142,144, # distinct matches = 8:
+	343.1, 342.1, 220.1, 74.1, 87.1, 84.2, 76.1, 221.1
+
+6,dial.c:145,146, # distinct matches = 2:
+	343.1, 342.1
+
+
+6,dial.c:156,163, # distinct matches = 1:
+	520.1
+
+6,dial.c:164,165, # distinct matches = 7:
+	487.1, 227.1, 229.1, 232.1, 497.1, 226.1, 520.1
+
+6,dial.c:166,167, # distinct matches = 6:
+	487.1, 227.1, 229.1, 232.1, 497.1, 226.1
+
+
+6,dial.c:191,192, # distinct matches = 3:
+	478.1, 476.1, 479.1
+
+6,dial.c:193,195, # distinct matches = 5:
+	503.1, 504.1, 478.1, 476.1, 479.1
+
+6,dial.c:196,197, # distinct matches = 2:
+	503.1, 504.1
+
+
+6,dial.c:202,202, # distinct matches = 1:
+	425.1
+
+6,dial.c:203,203, # distinct matches = 5:
+	418.2, 419.2, 420.2, 422.2, 425.1
+
+6,dial.c:204,204, # distinct matches = 7:
+	503.2, 332.1, 418.2, 419.2, 420.2, 422.2, 425.1
+
+6,dial.c:205,206, # distinct matches = 6:
+	503.2, 332.1, 418.2, 419.2, 420.2, 422.2
+
+6,dial.c:207,208, # distinct matches = 2:
+	503.2, 332.1
+
+
+6,dial.c:216,220, # distinct matches = 1:
+	333.1
+
+
+6,dial.c:226,229, # distinct matches = 1:
+	441.1
+
+
+6,dial.c:255,256, # distinct matches = 1:
+	502.1
+
+6,dial.c:257,259, # distinct matches = 2:
+	505.1, 502.1
+
+6,dial.c:260,274, # distinct matches = 1:
+	505.1
+
+6,dial.c:275,278, # distinct matches = 6:
+	86.1, 83.2, 75.1, 73.1, 221.2, 505.1
+
+
+6,dial.c:281,285, # distinct matches = 1:
+	17.1
+
+
+6,dial.c:294,303, # distinct matches = 1:
+	333.2
+
+
+6,dial.c:331,331, # distinct matches = 2:
+	34.2, 33.1
+
+6,dial.c:332,333, # distinct matches = 4:
+	217.1, 302.1, 34.2, 33.1
+
+6,dial.c:334,335, # distinct matches = 2:
+	217.1, 302.1
+
+6,dial.c:336,340, # distinct matches = 1:
+	217.1
+
+
+6,dial.c:342,346, # distinct matches = 1:
+	318.1
+
+
+6,dial.c:350,356, # distinct matches = 2:
+	355.1, 356.1
+
+6,dial.c:357,361, # distinct matches = 1:
+	97.1
+
+
+6,dial.c:364,366, # distinct matches = 2:
+	200.1, 583.1
+
+6,dial.c:367,373, # distinct matches = 1:
+	583.1
+
+6,dial.c:374,376, # distinct matches = 2:
+	355.2, 583.1
+
+6,dial.c:377,408, # distinct matches = 1:
+	583.1
+
+
+6,dial.c:420,420, # distinct matches = 2:
+	200.2, 265.1
+
+6,dial.c:421,422, # distinct matches = 3:
+	302.2, 200.2, 265.1
+
+6,dial.c:423,424, # distinct matches = 2:
+	302.2, 265.1
+
+
+6,dial.c:426,430, # distinct matches = 1:
+	181.1
+
+6,dial.c:431,438, # distinct matches = 1:
+	152.1
+
+
+6,dial.c:442,448, # distinct matches = 1:
+	4.1, 269.1
+
+6,dial.c:449,451, # distinct matches = 3:
+	160.1, 159.1, 4.1, 269.1
+
+6,dial.c:452,452, # distinct matches = 1:
+	4.1, 269.1
+
+
+6,dial.c:475,477, # distinct matches = 2:
+	161.1, 159.2, 4.1
+
+
+
+6,dial.c:485,491, # distinct matches = 1:
+	170.1
+
+
+6,dial.c:500,521, # distinct matches = 1:
+	2.1
+
+6,dial.c:522,529, # distinct matches = 5:
+	556.1, 345.1, 347.2, 558.1, 2.1
+
+6,dial.c:530,562, # distinct matches = 1:
+	2.1
+
+7,dial.thread.c:4,6, # distinct matches = 7:
+	132.2, 137.1, 138.2, 134.2, 135.2, 136.1, 133.2
+
+7,dial.thread.c:7,9, # distinct matches = 6:
+	132.2, 137.1, 134.2, 135.2, 136.1, 133.2
+
+
+7,dial.thread.c:24,49, # distinct matches = 1:
+	561.2
+
+
+7,dial.thread.c:64,67, # distinct matches = 1:
+	362.2
+
+
+7,dial.thread.c:85,105, # distinct matches = 1:
+	257.2
+
+7,dial.thread.c:106,109, # distinct matches = 3:
+	99.2, 101.1, 257.2
+
+7,dial.thread.c:110,112, # distinct matches = 1:
+	257.2
+
+7,dial.thread.c:113,115, # distinct matches = 2:
+	33.2, 257.2
+
+7,dial.thread.c:116,119, # distinct matches = 3:
+	100.2, 101.2, 257.2
+
+7,dial.thread.c:120,139, # distinct matches = 1:
+	257.2
+
+7,dial.thread.c:140,140, # distinct matches = 3:
+	343.2, 242.1, 257.2
+
+7,dial.thread.c:141,144, # distinct matches = 2:
+	343.2, 242.1
+
+
+7,dial.thread.c:149,149, # distinct matches = 4:
+	493.1, 491.1, 231.1, 232.2
+
+7,dial.thread.c:150,152, # distinct matches = 5:
+	520.2, 493.1, 491.1, 231.1, 232.2
+
+7,dial.thread.c:153,157, # distinct matches = 1:
+	520.2
+
+7,dial.thread.c:158,158, # distinct matches = 4:
+	479.2, 480.1, 223.1, 520.2
+
+7,dial.thread.c:159,159, # distinct matches = 5:
+	505.2, 479.2, 480.1, 223.1, 520.2
+
+7,dial.thread.c:160,161, # distinct matches = 4:
+	505.2, 479.2, 480.1, 223.1
+
+7,dial.thread.c:162,176, # distinct matches = 1:
+	505.2
+
+7,dial.thread.c:177,180, # distinct matches = 6:
+	82.2, 77.1, 78.1, 220.2, 85.1, 505.2
+
+
+7,dial.thread.c:185,189, # distinct matches = 1:
+	17.2
+
+
+7,dial.thread.c:228,233, # distinct matches = 2:
+	242.2, 342.2
+
+
+7,dial.thread.c:273,273, # distinct matches = 1:
+	502.2
+
+7,dial.thread.c:274,274, # distinct matches = 2:
+	3.1, 502.2
+
+7,dial.thread.c:275,282, # distinct matches = 4:
+	332.2, 504.2, 3.1, 502.2
+
+7,dial.thread.c:283,284, # distinct matches = 2:
+	332.2, 504.2
+
+
+7,dial.thread.c:313,317, # distinct matches = 1:
+	3.2
+
+
+7,dial.thread.c:328,331, # distinct matches = 1:
+	441.2
+
+7,dial.thread.c:332,335, # distinct matches = 2:
+	310.1, 217.2
+
+7,dial.thread.c:336,340, # distinct matches = 1:
+	217.2
+
+
+7,dial.thread.c:344,348, # distinct matches = 1:
+	318.2
+
+
+7,dial.thread.c:361,365, # distinct matches = 1:
+	97.2
+
+
+7,dial.thread.c:367,376, # distinct matches = 1:
+	583.2
+
+7,dial.thread.c:377,379, # distinct matches = 2:
+	356.2, 583.2
+
+7,dial.thread.c:380,411, # distinct matches = 1:
+	583.2
+
+
+7,dial.thread.c:419,419, # distinct matches = 1:
+	265.2
+
+7,dial.thread.c:420,424, # distinct matches = 2:
+	310.2, 265.2
+
+
+7,dial.thread.c:426,428, # distinct matches = 1:
+	181.2
+
+
+7,dial.thread.c:432,439, # distinct matches = 1:
+	152.2
+
+
+7,dial.thread.c:443,449, # distinct matches = 1:
+	269.2, 4.2 (coincides with 269.2)
+
+7,dial.thread.c:450,452, # distinct matches = 3:
+	162.1, 161.2, 269.2, 4.2 (coincides with 269.2)
+
+7,dial.thread.c:453,453, # distinct matches = 1:
+	269.2, 4.2 (coincides with 269.2)
+
+
+7,dial.thread.c:476,478, # distinct matches = 2:
+	162.2, 160.2, 4.2 (coincides with 269.2)
+
+
+
+7,dial.thread.c:486,493, # distinct matches = 1:
+	170.2
+
+7,dial.thread.c:494,515, # distinct matches = 1:
+	2.2
+
+7,dial.thread.c:516,523, # distinct matches = 5:
+	346.1, 557.1, 559.1, 348.2, 2.2
+
+7,dial.thread.c:524,553, # distinct matches = 1:
+	2.2
+
+
+8,esmprint.c:6,9, # distinct matches = 2:
+	154.1, 155.1
+
+8,esmprint.c:10,12, # distinct matches = 2:
+	36.1, 154.1
+
+
+9,netssh.c:122,122, # distinct matches = 1:
+	598.1
+
+9,netssh.c:123,126, # distinct matches = 1:
+	506.1, 598.1
+
+9,netssh.c:127,128, # distinct matches = 2:
+	154.2, 506.1, 598.1
+
+9,netssh.c:129,131, # distinct matches = 2:
+	154.2, 506.1
+
+9,netssh.c:132,134, # distinct matches = 2:
+	35.1, 154.2
+
+9,netssh.c:135,136, # distinct matches = 1:
+	35.1
+
+9,netssh.c:137,137, # distinct matches = 1:
+	110.1
+
+9,netssh.c:138,138, # distinct matches = 2:
+	598.2, 110.1
+
+9,netssh.c:139,141, # distinct matches = 2:
+	506.2, 598.2 (coincides with 506.2), 110.1
+
+9,netssh.c:142,142, # distinct matches = 1:
+	506.2, 598.2 (coincides with 506.2)
+
+9,netssh.c:143,144, # distinct matches = 2:
+	155.2, 506.2, 598.2 (coincides with 506.2)
+
+9,netssh.c:145,145, # distinct matches = 2:
+	155.2, 506.2
+
+
+9,netssh.c:151,154, # distinct matches = 2:
+	35.2, 36.2
+
+9,netssh.c:155,155, # distinct matches = 2:
+	110.2, 35.2
+
+9,netssh.c:156,158, # distinct matches = 1:
+	110.2
+
+9,netssh.c:159,159, # distinct matches = 4:
+	27.1, 26.1, 25.1, 110.2
+
+9,netssh.c:160,161, # distinct matches = 3:
+	27.1, 26.1, 25.1
+
+9,netssh.c:162,163, # distinct matches = 1:
+	25.1
+
+9,netssh.c:164,168, # distinct matches = 3:
+	514.1, 239.1, 25.1
+
+
+9,netssh.c:175,175, # distinct matches = 1:
+	352.1
+
+9,netssh.c:176,179, # distinct matches = 2:
+	144.1, 352.1
+
+
+9,netssh.c:183,189, # distinct matches = 1:
+	151.1
+
+9,netssh.c:190,192, # distinct matches = 2:
+	564.1, 151.1
+
+9,netssh.c:193,194, # distinct matches = 1:
+	151.1
+
+
+9,netssh.c:213,221, # distinct matches = 1:
+	515.1
+
+9,netssh.c:222,224, # distinct matches = 2:
+	515.2, 515.1
+
+9,netssh.c:225,233, # distinct matches = 1:
+	515.2
+
+
+9,netssh.c:311,311, # distinct matches = 1:
+	609.1
+
+9,netssh.c:312,312, # distinct matches = 4:
+	602.1, 261.1, 608.1, 599.1, 609.1
+
+9,netssh.c:313,315, # distinct matches = 6:
+	335.1, 511.1, 602.1, 261.1, 608.1, 599.1, 609.1
+
+9,netssh.c:316,317, # distinct matches = 2:
+	335.1, 511.1, 599.1
+
+
+9,netssh.c:328,330, # distinct matches = 2:
+	92.1, 91.1
+
+
+9,netssh.c:334,337, # distinct matches = 1:
+	184.1
+
+
+9,netssh.c:361,363, # distinct matches = 8:
+	462.1, 454.1, 459.1, 460.1, 461.1, 450.1, 449.1, 447.1
+
+9,netssh.c:364,364, # distinct matches = 6:
+	454.1, 459.1, 460.1, 450.1, 449.1, 447.1
+
+9,netssh.c:365,366, # distinct matches = 1:
+	447.1
+
+
+9,netssh.c:377,377, # distinct matches = 1:
+	353.1
+
+9,netssh.c:378,378, # distinct matches = 4:
+	548.1, 547.1, 549.1, 353.1
+
+9,netssh.c:379,379, # distinct matches = 15:
+	40.1, 45.1, 46.1, 44.1, 43.1, 47.1, 48.1, 49.1, 42.1, 41.1, 39.1, 548.1, 547.1, 549.1, 353.1
+
+9,netssh.c:380,380, # distinct matches = 18:
+	256.1, 574.1, 255.1, 40.1, 45.1, 46.1, 44.1, 43.1, 47.1, 48.1, 49.1, 42.1, 41.1, 39.1, 548.1, 547.1, 549.1, 353.1
+
+9,netssh.c:381,381, # distinct matches = 20:
+	493.2, 492.1, 496.1, 495.1, 494.1, 497.2, 256.1, 574.1, 255.1, 40.1, 45.1, 46.1, 44.1, 43.1, 47.1, 48.1, 49.1, 42.1, 41.1, 39.1
+
+9,netssh.c:382,382, # distinct matches = 13:
+	493.2, 492.1, 496.1, 495.1, 494.1, 497.2, 256.1, 574.1, 255.1, 40.1, 42.1, 41.1, 39.1
+
+9,netssh.c:383,384, # distinct matches = 6:
+	493.2, 492.1, 496.1, 495.1, 494.1, 497.2
+
+
+9,netssh.c:387,389, # distinct matches = 1:
+	65.1
+
+
+9,netssh.c:391,391, # distinct matches = 3:
+	262.1, 606.1, 600.1
+
+9,netssh.c:392,394, # distinct matches = 7:
+	507.1, 238.1, 237.1, 508.1, 262.1, 606.1, 600.1
+
+9,netssh.c:395,396, # distinct matches = 5:
+	507.1, 238.1, 237.1, 508.1, 600.1
+
+
+9,netssh.c:405,411, # distinct matches = 3:
+	595.1, 597.1, 596.1
+
+
+9,netssh.c:418,421, # distinct matches = 2:
+	216.1, 215.1
+
+
+9,netssh.c:473,475, # distinct matches = 1:
+	298.1
+
+9,netssh.c:476,479, # distinct matches = 3:
+	264.1, 604.1, 298.1
+
+9,netssh.c:480,483, # distinct matches = 1:
+	298.1
+
+
+9,netssh.c:488,489, # distinct matches = 3:
+	295.1, 211.1, 294.1
+
+9,netssh.c:490,492, # distinct matches = 4:
+	597.2, 295.1, 211.1, 294.1
+
+9,netssh.c:493,495, # distinct matches = 9:
+	456.1, 458.1, 460.2, 464.1, 467.1, 451.1, 597.2, 295.1, 294.1
+
+9,netssh.c:496,496, # distinct matches = 6:
+	456.1, 458.1, 460.2, 451.1, 295.1, 294.1
+
+9,netssh.c:497,499, # distinct matches = 2:
+	451.1, 294.1
+
+
+9,netssh.c:516,519, # distinct matches = 2:
+	213.1, 63.1
+
+
+9,netssh.c:550,550, # distinct matches = 2:
+	50.1, 51.1
+
+9,netssh.c:551,552, # distinct matches = 8:
+	607.1, 603.1, 604.2, 605.1, 606.2, 608.2, 50.1, 51.1
+
+9,netssh.c:553,554, # distinct matches = 6:
+	607.1, 603.1, 604.2, 605.1, 606.2, 608.2
+
+
+9,netssh.c:561,570, # distinct matches = 1:
+	1.1
+
+
+9,netssh.c:579,581, # distinct matches = 1:
+	350.1
+
+
+9,netssh.c:591,592, # distinct matches = 5:
+	376.2, 371.1, 202.1, 373.2, 276.1
+
+9,netssh.c:593,593, # distinct matches = 6:
+	359.1, 376.2, 371.1, 202.1, 373.2, 276.1
+
+9,netssh.c:594,594, # distinct matches = 3:
+	551.1, 359.1, 202.1
+
+9,netssh.c:595,595, # distinct matches = 2:
+	551.1, 359.1
+
+9,netssh.c:596,596, # distinct matches = 3:
+	37.1, 551.1, 359.1
+
+9,netssh.c:597,599, # distinct matches = 4:
+	335.2, 510.1, 334.1, 37.1
+
+9,netssh.c:600,601, # distinct matches = 3:
+	335.2, 510.1, 334.1
+
+9,netssh.c:602,603, # distinct matches = 2:
+	93.1, 334.1
+
+9,netssh.c:604,605, # distinct matches = 1:
+	93.1
+
+
+9,netssh.c:608,608, # distinct matches = 1:
+	585.1
+
+9,netssh.c:609,610, # distinct matches = 4:
+	430.1, 431.1, 429.1, 585.1
+
+9,netssh.c:611,613, # distinct matches = 8:
+	462.2, 467.2, 468.1, 466.1, 430.1, 431.1, 429.1, 585.1
+
+
+9,netssh.c:619,623, # distinct matches = 1:
+	109.1
+
+
+9,netssh.c:626,627, # distinct matches = 1:
+	146.1
+
+9,netssh.c:628,629, # distinct matches = 9:
+	389.1, 280.1, 282.1, 292.1, 204.1, 390.1, 46.2, 281.1, 146.1
+
+9,netssh.c:630,630, # distinct matches = 10:
+	541.1, 389.1, 280.1, 282.1, 292.1, 204.1, 390.1, 46.2, 281.1, 146.1
+
+9,netssh.c:631,632, # distinct matches = 1:
+	541.1
+
+
+9,netssh.c:645,647, # distinct matches = 4:
+	545.1, 549.2, 542.1, 543.1
+
+9,netssh.c:648,648, # distinct matches = 3:
+	146.2, 542.1, 543.1
+
+9,netssh.c:649,649, # distinct matches = 1:
+	146.2
+
+9,netssh.c:650,652, # distinct matches = 9:
+	283.1, 203.1, 291.1, 387.1, 388.1, 45.2, 285.1, 284.1, 146.2
+
+
+9,netssh.c:658,660, # distinct matches = 9:
+	392.1, 391.1, 282.2, 279.1, 289.1, 47.2, 283.2, 278.1, 288.1
+
+
+9,netssh.c:662,664, # distinct matches = 1:
+	60.1
+
+9,netssh.c:665,665, # distinct matches = 4:
+	382.1, 381.1, 42.2, 60.1
+
+9,netssh.c:666,668, # distinct matches = 7:
+	580.1, 247.1, 246.1, 382.1, 381.1, 42.2, 60.1
+
+
+9,netssh.c:671,673, # distinct matches = 9:
+	396.1, 279.2, 208.1, 287.1, 281.2, 207.1, 285.2, 48.2, 395.1
+
+
+9,netssh.c:676,681, # distinct matches = 1:
+	188.1
+
+
+9,netssh.c:687,687, # distinct matches = 1:
+	38.1
+
+9,netssh.c:688,689, # distinct matches = 4:
+	544.1, 542.2, 547.2, 38.1
+
+9,netssh.c:690,690, # distinct matches = 5:
+	573.1, 544.1, 542.2, 547.2, 38.1
+
+9,netssh.c:691,691, # distinct matches = 3:
+	573.1, 544.1, 542.2
+
+9,netssh.c:692,692, # distinct matches = 1:
+	573.1
+
+9,netssh.c:693,693, # distinct matches = 2:
+	184.2, 573.1
+
+9,netssh.c:694,696, # distinct matches = 1:
+	184.2
+
+
+9,netssh.c:706,706, # distinct matches = 1:
+	353.2
+
+9,netssh.c:707,708, # distinct matches = 4:
+	543.2, 544.2, 546.1, 353.2
+
+9,netssh.c:709,709, # distinct matches = 5:
+	188.2, 543.2, 544.2, 546.1, 353.2
+
+9,netssh.c:710,710, # distinct matches = 3:
+	188.2, 543.2, 544.2
+
+9,netssh.c:711,715, # distinct matches = 1:
+	188.2
+
+
+9,netssh.c:718,718, # distinct matches = 1:
+	38.2
+
+9,netssh.c:719,720, # distinct matches = 4:
+	546.2, 545.2, 548.2, 38.2
+
+9,netssh.c:721,721, # distinct matches = 5:
+	562.1, 546.2, 545.2, 548.2, 38.2
+
+9,netssh.c:722,722, # distinct matches = 2:
+	91.2, 562.1
+
+9,netssh.c:723,723, # distinct matches = 12:
+	39.2, 391.2, 389.2, 395.2, 387.2, 385.1, 383.1, 381.2, 380.1, 393.1, 91.2, 562.1
+
+9,netssh.c:724,724, # distinct matches = 15:
+	254.1, 253.1, 575.1, 39.2, 391.2, 389.2, 395.2, 387.2, 385.1, 383.1, 381.2, 380.1, 393.1, 91.2, 562.1
+
+9,netssh.c:725,725, # distinct matches = 17:
+	484.1, 482.1, 483.1, 254.1, 253.1, 575.1, 39.2, 391.2, 389.2, 395.2, 387.2, 385.1, 383.1, 381.2, 380.1, 393.1, 562.1
+
+9,netssh.c:726,726, # distinct matches = 11:
+	507.2, 484.1, 482.1, 483.1, 254.1, 253.1, 575.1, 39.2, 381.2, 380.1, 562.1
+
+9,netssh.c:727,728, # distinct matches = 4:
+	507.2, 484.1, 482.1, 483.1
+
+9,netssh.c:729,730, # distinct matches = 1:
+	507.2
+
+
+9,netssh.c:732,734, # distinct matches = 1:
+	95.1
+
+
+9,netssh.c:739,740, # distinct matches = 1:
+	594.1
+
+9,netssh.c:741,742, # distinct matches = 3:
+	428.1, 431.2, 594.1
+
+9,netssh.c:743,745, # distinct matches = 7:
+	461.2, 465.1, 464.2, 463.1, 428.1, 431.2, 594.1
+
+
+9,netssh.c:750,753, # distinct matches = 1:
+	109.2
+
+9,netssh.c:754,754, # distinct matches = 2:
+	541.2, 109.2
+
+9,netssh.c:755,756, # distinct matches = 1:
+	541.2
+
+
+9,netssh.c:758,761, # distinct matches = 2:
+	67.1, 214.1
+
+
+9,netssh.c:793,793, # distinct matches = 3:
+	498.1, 499.1, 500.1
+
+9,netssh.c:794,795, # distinct matches = 10:
+	574.2, 575.2, 576.1, 578.1, 579.1, 580.2, 577.1, 498.1, 499.1, 500.1
+
+9,netssh.c:796,796, # distinct matches = 9:
+	551.2, 550.1, 574.2, 575.2, 576.1, 578.1, 579.1, 580.2, 577.1
+
+9,netssh.c:797,798, # distinct matches = 4:
+	70.1, 66.1, 551.2, 550.1
+
+9,netssh.c:799,799, # distinct matches = 3:
+	70.1, 66.1, 550.1
+
+9,netssh.c:800,800, # distinct matches = 1:
+	66.1
+
+
+9,netssh.c:806,807, # distinct matches = 1:
+	65.2
+
+9,netssh.c:808,808, # distinct matches = 2:
+	360.1, 65.2
+
+9,netssh.c:809,810, # distinct matches = 1:
+	360.1
+
+
+9,netssh.c:819,821, # distinct matches = 1:
+	360.2
+
+
+9,netssh.c:828,830, # distinct matches = 3:
+	415.1, 69.1, 66.2
+
+9,netssh.c:831,831, # distinct matches = 1:
+	66.2
+
+
+9,netssh.c:835,835, # distinct matches = 4:
+	415.2, 68.1, 214.2, 70.2
+
+9,netssh.c:836,837, # distinct matches = 12:
+	290.1, 291.2, 292.2, 383.2, 208.2, 384.1, 43.2, 289.2, 415.2, 68.1, 214.2, 70.2
+
+9,netssh.c:838,838, # distinct matches = 10:
+	290.1, 291.2, 292.2, 383.2, 208.2, 384.1, 43.2, 289.2, 68.1, 214.2
+
+
+9,netssh.c:841,844, # distinct matches = 2:
+	64.1, 63.2
+
+
+9,netssh.c:853,861, # distinct matches = 1:
+	1.2
+
+
+9,netssh.c:865,865, # distinct matches = 1:
+	350.2
+
+9,netssh.c:866,866, # distinct matches = 2:
+	359.2, 350.2
+
+9,netssh.c:867,867, # distinct matches = 3:
+	550.2, 359.2, 350.2
+
+9,netssh.c:868,868, # distinct matches = 5:
+	69.2, 67.2, 68.2, 550.2, 359.2
+
+9,netssh.c:869,869, # distinct matches = 13:
+	393.2, 278.2, 280.2, 284.2, 286.1, 207.2, 394.1, 49.2, 69.2, 67.2, 68.2, 550.2, 359.2
+
+9,netssh.c:870,870, # distinct matches = 13:
+	581.1, 393.2, 278.2, 280.2, 284.2, 286.1, 207.2, 394.1, 49.2, 69.2, 67.2, 68.2, 550.2
+
+9,netssh.c:871,871, # distinct matches = 11:
+	581.1, 393.2, 278.2, 280.2, 284.2, 286.1, 207.2, 394.1, 49.2, 67.2, 68.2
+
+9,netssh.c:872,873, # distinct matches = 5:
+	602.2, 600.2, 601.1, 603.2, 581.1
+
+9,netssh.c:874,875, # distinct matches = 4:
+	602.2, 600.2, 601.1, 603.2
+
+9,netssh.c:876,877, # distinct matches = 1:
+	600.2
+
+9,netssh.c:878,878, # distinct matches = 1:
+	95.2
+
+9,netssh.c:879,880, # distinct matches = 2:
+	94.1, 95.2
+
+9,netssh.c:881,881, # distinct matches = 1:
+	94.1
+
+9,netssh.c:882,883, # distinct matches = 3:
+	294.2, 296.1, 94.1
+
+9,netssh.c:884,886, # distinct matches = 4:
+	596.2, 294.2, 296.1, 94.1
+
+9,netssh.c:887,889, # distinct matches = 9:
+	466.2, 463.2, 459.2, 457.1, 455.1, 452.1, 596.2, 294.2, 94.1
+
+9,netssh.c:890,890, # distinct matches = 6:
+	459.2, 457.1, 455.1, 452.1, 294.2, 94.1
+
+9,netssh.c:891,893, # distinct matches = 2:
+	452.1, 294.2
+
+
+9,netssh.c:898,898, # distinct matches = 1:
+	205.1
+
+9,netssh.c:899,899, # distinct matches = 2:
+	215.2, 205.1
+
+9,netssh.c:900,902, # distinct matches = 3:
+	51.2, 215.2, 205.1
+
+
+9,netssh.c:904,906, # distinct matches = 1:
+	111.1
+
+
+9,netssh.c:910,913, # distinct matches = 2:
+	213.2, 64.2
+
+
+9,netssh.c:916,916, # distinct matches = 1:
+	205.2
+
+9,netssh.c:917,917, # distinct matches = 2:
+	216.2, 205.2
+
+9,netssh.c:918,920, # distinct matches = 3:
+	50.2, 216.2, 205.2
+
+
+9,netssh.c:922,922, # distinct matches = 1:
+	111.2
+
+9,netssh.c:923,923, # distinct matches = 7:
+	599.2 (coincides with 261.2), 601.2, 264.2, 263.1, 262.2, 261.2, 607.2, 111.2
+
+9,netssh.c:924,924, # distinct matches = 9:
+	334.2, 509.1, 599.2 (coincides with 261.2), 601.2, 264.2, 263.1, 262.2, 261.2, 607.2, 111.2
+
+9,netssh.c:925,926, # distinct matches = 8:
+	334.2, 509.1, 599.2 (coincides with 261.2), 601.2, 264.2, 263.1, 262.2, 261.2, 607.2
+
+9,netssh.c:927,928, # distinct matches = 2:
+	334.2, 509.1, 599.2 (coincides with 261.2)
+
+9,netssh.c:929,930, # distinct matches = 1:
+	334.2
+
+
+9,netssh.c:934,934, # distinct matches = 1:
+	585.2
+
+9,netssh.c:935,936, # distinct matches = 4:
+	426.1, 427.1, 428.2, 585.2
+
+9,netssh.c:937,939, # distinct matches = 8:
+	448.1, 455.2, 456.2, 447.2, 426.1, 427.1, 428.2, 585.2
+
+9,netssh.c:940,940, # distinct matches = 6:
+	448.1, 455.2, 456.2, 447.2, 426.1, 427.1
+
+9,netssh.c:941,942, # distinct matches = 1:
+	447.2
+
+
+9,netssh.c:954,956, # distinct matches = 9:
+	385.2, 288.2, 203.2, 290.2, 204.2, 287.2, 44.2, 286.2, 386.1
+
+9,netssh.c:957,959, # distinct matches = 1:
+	60.2
+
+9,netssh.c:960,960, # distinct matches = 4:
+	380.2, 41.2, 379.1, 60.2
+
+9,netssh.c:961,963, # distinct matches = 7:
+	577.2, 249.1, 250.1, 380.2, 41.2, 379.1, 60.2
+
+
+9,netssh.c:965,967, # distinct matches = 1:
+	573.2
+
+9,netssh.c:968,968, # distinct matches = 2:
+	562.2, 573.2
+
+9,netssh.c:969,969, # distinct matches = 2:
+	92.2, 562.2
+
+9,netssh.c:970,970, # distinct matches = 12:
+	379.2, 390.2, 388.2, 40.2, 394.2, 386.2, 384.2, 382.2, 396.2, 392.2, 92.2, 562.2
+
+9,netssh.c:971,971, # distinct matches = 15:
+	576.2, 252.1, 251.1, 379.2, 390.2, 388.2, 40.2, 394.2, 386.2, 384.2, 382.2, 396.2, 392.2, 92.2, 562.2
+
+9,netssh.c:972,972, # distinct matches = 17:
+	478.2, 477.1, 480.2, 576.2, 252.1, 251.1, 379.2, 390.2, 388.2, 40.2, 394.2, 386.2, 384.2, 382.2, 396.2, 392.2, 562.2
+
+9,netssh.c:973,973, # distinct matches = 10:
+	478.2, 477.1, 480.2, 576.2, 252.1, 251.1, 379.2, 40.2, 382.2, 562.2
+
+9,netssh.c:974,975, # distinct matches = 3:
+	478.2, 477.1, 480.2
+
+
+9,netssh.c:1003,1006, # distinct matches = 1:
+	22.1
+
+
+9,netssh.c:1012,1015, # distinct matches = 1:
+	22.2
+
+
+9,netssh.c:1047,1049, # distinct matches = 1:
+	610.1
+
+
+9,netssh.c:1056,1059, # distinct matches = 1:
+	206.1
+
+
+9,netssh.c:1061,1061, # distinct matches = 1:
+	164.1, 147.1
+
+9,netssh.c:1062,1066, # distinct matches = 3:
+	238.2, 235.1, 164.1, 147.1
+
+9,netssh.c:1067,1067, # distinct matches = 2:
+	235.1, 164.1, 147.1
+
+9,netssh.c:1068,1070, # distinct matches = 2:
+	235.1, 147.1
+
+
+9,netssh.c:1076,1077, # distinct matches = 1:
+	593.1
+
+9,netssh.c:1078,1079, # distinct matches = 3:
+	427.2, 429.2, 593.1
+
+9,netssh.c:1080,1083, # distinct matches = 7:
+	452.2, 453.1, 454.2, 451.2, 427.2, 429.2, 593.1
+
+9,netssh.c:1084,1084, # distinct matches = 6:
+	452.2, 453.1, 454.2, 451.2, 427.2, 593.1
+
+9,netssh.c:1085,1085, # distinct matches = 2:
+	452.2, 451.2
+
+9,netssh.c:1086,1087, # distinct matches = 4:
+	358.1, 357.1, 452.2, 451.2
+
+9,netssh.c:1088,1088, # distinct matches = 2:
+	358.1, 357.1
+
+9,netssh.c:1089,1095, # distinct matches = 1:
+	357.1
+
+
+9,netssh.c:1123,1129, # distinct matches = 1:
+	275.1
+
+
+9,netssh.c:1145,1147, # distinct matches = 4:
+	612.1, 613.1, 615.1, 614.1
+
+
+9,netssh.c:1158,1163, # distinct matches = 1:
+	275.2
+
+
+9,netssh.c:1186,1188, # distinct matches = 4:
+	267.1, 615.2, 268.1, 266.1
+
+9,netssh.c:1189,1189, # distinct matches = 3:
+	267.1, 268.1, 266.1
+
+
+9,netssh.c:1204,1204, # distinct matches = 4:
+	372.1, 370.1, 368.2, 371.2
+
+9,netssh.c:1205,1206, # distinct matches = 5:
+	474.1, 372.1, 370.1, 368.2, 371.2
+
+9,netssh.c:1207,1207, # distinct matches = 1:
+	474.1
+
+
+9,netssh.c:1220,1222, # distinct matches = 5:
+	377.2, 374.2, 277.1, 202.2, 372.2
+
+9,netssh.c:1223,1223, # distinct matches = 1:
+	202.2
+
+9,netssh.c:1224,1226, # distinct matches = 2:
+	105.1, 106.1
+
+9,netssh.c:1227,1229, # distinct matches = 2:
+	311.1, 105.1
+
+
+9,netssh.c:1231,1232, # distinct matches = 1:
+	15.1
+
+9,netssh.c:1233,1235, # distinct matches = 2:
+	182.1, 15.1
+
+9,netssh.c:1236,1238, # distinct matches = 4:
+	267.2, 614.2, 182.1, 15.1
+
+9,netssh.c:1239,1239, # distinct matches = 3:
+	267.2, 182.1, 15.1
+
+
+9,netssh.c:1245,1247, # distinct matches = 3:
+	234.1, 498.2, 186.1
+
+
+9,netssh.c:1249,1250, # distinct matches = 1:
+	298.2
+
+9,netssh.c:1251,1251, # distinct matches = 3:
+	609.2, 610.2, 298.2
+
+9,netssh.c:1252,1252, # distinct matches = 5:
+	605.2, 263.2, 609.2, 610.2, 298.2
+
+9,netssh.c:1253,1253, # distinct matches = 7:
+	236.1, 235.2, 605.2, 263.2, 609.2, 610.2, 298.2
+
+9,netssh.c:1254,1255, # distinct matches = 6:
+	236.1, 235.2, 605.2, 263.2, 609.2, 298.2
+
+9,netssh.c:1256,1259, # distinct matches = 3:
+	236.1, 235.2, 298.2
+
+9,netssh.c:1260,1261, # distinct matches = 2:
+	236.1, 235.2
+
+
+9,netssh.c:1264,1265, # distinct matches = 3:
+	296.2, 297.1, 211.2
+
+9,netssh.c:1266,1267, # distinct matches = 5:
+	594.2, 593.2, 296.2, 297.1, 211.2
+
+9,netssh.c:1268,1268, # distinct matches = 7:
+	426.2, 430.2, 594.2, 593.2, 296.2, 297.1, 211.2
+
+9,netssh.c:1269,1269, # distinct matches = 4:
+	426.2, 430.2, 594.2, 593.2
+
+9,netssh.c:1270,1272, # distinct matches = 8:
+	449.2, 446.1, 457.2, 458.2, 426.2, 430.2, 594.2, 593.2
+
+9,netssh.c:1273,1273, # distinct matches = 6:
+	449.2, 446.1, 457.2, 458.2, 426.2, 593.2
+
+9,netssh.c:1274,1276, # distinct matches = 2:
+	357.2, 446.1
+
+9,netssh.c:1277,1282, # distinct matches = 1:
+	357.2
+
+
+9,netssh.c:1310,1310, # distinct matches = 1:
+	16.1
+
+9,netssh.c:1311,1315, # distinct matches = 1:
+	416.1, 16.1
+
+9,netssh.c:1316,1317, # distinct matches = 2:
+	401.1, 416.1, 16.1
+
+9,netssh.c:1318,1319, # distinct matches = 2:
+	401.1, 416.1
+
+
+9,netssh.c:1324,1324, # distinct matches = 1:
+	32.1
+
+9,netssh.c:1325,1326, # distinct matches = 2:
+	32.2, 32.1
+
+9,netssh.c:1327,1327, # distinct matches = 1:
+	32.2
+
+
+9,netssh.c:1333,1336, # distinct matches = 2:
+	401.2, 402.1
+
+
+9,netssh.c:1339,1339, # distinct matches = 1:
+	16.2
+
+9,netssh.c:1340,1340, # distinct matches = 1:
+	416.2, 16.2 (coincides with 416.2)
+
+9,netssh.c:1341,1342, # distinct matches = 2:
+	402.2, 416.2, 16.2 (coincides with 416.2)
+
+9,netssh.c:1343,1344, # distinct matches = 2:
+	402.2, 416.2
+
+
+9,netssh.c:1356,1358, # distinct matches = 5:
+	277.2, 378.2, 370.2, 369.2, 276.2
+
+9,netssh.c:1359,1359, # distinct matches = 1:
+	164.2 (coincides with 147.2), 147.2
+
+9,netssh.c:1360,1364, # distinct matches = 3:
+	237.2, 236.2, 164.2 (coincides with 147.2), 147.2
+
+9,netssh.c:1365,1365, # distinct matches = 2:
+	236.2, 164.2 (coincides with 147.2), 147.2
+
+9,netssh.c:1366,1366, # distinct matches = 2:
+	236.2, 147.2
+
+9,netssh.c:1367,1368, # distinct matches = 3:
+	94.2, 236.2, 147.2
+
+9,netssh.c:1369,1369, # distinct matches = 1:
+	94.2
+
+9,netssh.c:1370,1371, # distinct matches = 3:
+	297.2, 295.2, 94.2
+
+9,netssh.c:1372,1374, # distinct matches = 4:
+	595.2, 297.2, 295.2, 94.2
+
+9,netssh.c:1375,1377, # distinct matches = 9:
+	468.2, 448.2, 465.2, 453.2, 446.2, 450.2, 595.2, 295.2, 94.2
+
+9,netssh.c:1378,1378, # distinct matches = 6:
+	448.2, 453.2, 446.2, 450.2, 295.2, 94.2
+
+9,netssh.c:1379,1381, # distinct matches = 2:
+	358.2, 446.2
+
+
+9,netssh.c:1410,1413, # distinct matches = 1:
+	93.2
+
+
+9,netssh.c:1441,1442, # distinct matches = 1:
+	15.2
+
+9,netssh.c:1443,1445, # distinct matches = 2:
+	183.1, 15.2
+
+9,netssh.c:1446,1448, # distinct matches = 4:
+	268.2, 613.2, 183.1, 15.2
+
+9,netssh.c:1449,1449, # distinct matches = 3:
+	268.2, 183.1, 15.2
+
+
+9,netssh.c:1460,1463, # distinct matches = 1:
+	349.1
+
+
+9,netssh.c:1478,1480, # distinct matches = 1:
+	474.2
+
+9,netssh.c:1481,1481, # distinct matches = 2:
+	105.2, 474.2
+
+9,netssh.c:1482,1486, # distinct matches = 1:
+	105.2
+
+
+9,netssh.c:1489,1491, # distinct matches = 2:
+	182.2, 183.2
+
+9,netssh.c:1492,1494, # distinct matches = 4:
+	612.2, 266.2, 182.2, 183.2
+
+9,netssh.c:1495,1495, # distinct matches = 6:
+	485.1, 224.1, 482.2, 266.2, 182.2, 183.2
+
+9,netssh.c:1496,1498, # distinct matches = 6:
+	510.2, 511.2, 509.2, 485.1, 224.1, 482.2
+
+9,netssh.c:1499,1500, # distinct matches = 3:
+	510.2, 511.2, 509.2
+
+
+9,netssh.c:1524,1527, # distinct matches = 1:
+	37.2
+
+
+9,netssh.c:1582,1585, # distinct matches = 3:
+	21.1, 24.1, 19.1
+
+9,netssh.c:1586,1589, # distinct matches = 3:
+	21.2, 20.1, 19.1
+
+
+9,netssh.c:1672,1674, # distinct matches = 2:
+	244.1, 245.1
+
+
+9,netssh.c:1687,1691, # distinct matches = 1:
+	470.2
+
+
+9,netssh.c:1693,1695, # distinct matches = 1:
+	222.1
+
+
+9,netssh.c:1701,1703, # distinct matches = 7:
+	256.2, 246.2, 248.1, 579.2, 252.2, 253.2, 249.2
+
+
+9,netssh.c:1721,1726, # distinct matches = 1:
+	72.1
+
+
+9,netssh.c:1758,1762, # distinct matches = 1:
+	243.1
+
+
+9,netssh.c:1782,1784, # distinct matches = 1:
+	150.1
+
+
+9,netssh.c:1789,1791, # distinct matches = 1:
+	150.2
+
+
+9,netssh.c:1798,1800, # distinct matches = 3:
+	432.1, 433.1, 434.1
+
+
+9,netssh.c:1802,1811, # distinct matches = 5:
+	167.2, 218.1, 307.2, 417.1, 304.2, 300.2
+
+
+9,netssh.c:1814,1817, # distinct matches = 1:
+	592.1
+
+
+9,netssh.c:1819,1822, # distinct matches = 1:
+	206.2
+
+
+9,netssh.c:1830,1834, # distinct matches = 5:
+	301.2, 305.2, 219.2, 417.2 (coincides with 218.2), 308.2, 218.2
+
+
+
+9,netssh.c:1856,1860, # distinct matches = 1:
+	414.1
+
+
+9,netssh.c:1865,1867, # distinct matches = 1:
+	29.1
+
+
+9,netssh.c:1897,1899, # distinct matches = 1:
+	29.2
+
+
+9,netssh.c:1908,1913, # distinct matches = 1:
+	72.2
+
+
+9,netssh.c:1922,1924, # distinct matches = 1:
+	354.1
+
+
+9,netssh.c:1965,1977, # distinct matches = 1:
+	414.2
+
+9,netssh.c:1978,1981, # distinct matches = 1:
+	592.2
+
+
+9,netssh.c:1995,1997, # distinct matches = 1:
+	354.2
+
+
+9,netssh.c:2065,2069, # distinct matches = 1:
+	243.2
+
+
+9,netssh.c:2072,2073, # distinct matches = 1:
+	102.1
+
+9,netssh.c:2074,2074, # distinct matches = 2:
+	331.1, 102.1
+
+9,netssh.c:2075,2078, # distinct matches = 1:
+	331.1
+
+
+9,netssh.c:2082,2087, # distinct matches = 2:
+	165.1, 148.1
+
+9,netssh.c:2088,2089, # distinct matches = 1:
+	148.1
+
+
+9,netssh.c:2132,2135, # distinct matches = 2:
+	104.1, 103.1
+
+
+9,netssh.c:2142,2147, # distinct matches = 1:
+	31.1
+
+9,netssh.c:2148,2150, # distinct matches = 1:
+	611.1
+
+
+9,netssh.c:2155,2157, # distinct matches = 3:
+	158.1, 472.1, 157.1
+
+
+9,netssh.c:2165,2165, # distinct matches = 3:
+	158.2, 471.1, 156.1
+
+9,netssh.c:2166,2167, # distinct matches = 4:
+	104.2, 158.2, 471.1, 156.1
+
+9,netssh.c:2168,2169, # distinct matches = 2:
+	104.2, 471.1
+
+
+9,netssh.c:2185,2190, # distinct matches = 1:
+	31.2
+
+
+9,netssh.c:2205,2205, # distinct matches = 3:
+	473.1, 157.2, 156.2
+
+9,netssh.c:2206,2207, # distinct matches = 4:
+	102.2, 473.1, 157.2, 156.2
+
+9,netssh.c:2208,2208, # distinct matches = 4:
+	500.2, 233.1, 186.2, 102.2
+
+9,netssh.c:2209,2209, # distinct matches = 4:
+	582.1, 500.2, 233.1, 186.2
+
+9,netssh.c:2210,2210, # distinct matches = 8:
+	554.2, 553.1, 557.2, 556.2, 582.1, 500.2, 233.1, 186.2
+
+9,netssh.c:2211,2211, # distinct matches = 9:
+	79.2, 87.2, 86.2, 85.2, 554.2, 553.1, 557.2, 556.2, 582.1
+
+9,netssh.c:2212,2214, # distinct matches = 10:
+	337.1, 79.2, 87.2, 86.2, 85.2, 554.2, 553.1, 557.2, 556.2, 582.1
+
+9,netssh.c:2215,2216, # distinct matches = 1:
+	337.1
+
+
+9,netssh.c:2227,2228, # distinct matches = 1:
+	367.1
+
+9,netssh.c:2229,2229, # distinct matches = 2:
+	331.2, 367.1
+
+9,netssh.c:2230,2233, # distinct matches = 1:
+	331.2
+
+
+9,netssh.c:2244,2249, # distinct matches = 2:
+	148.2, 149.1
+
+9,netssh.c:2250,2251, # distinct matches = 1:
+	148.2
+
+
+9,netssh.c:2262,2267, # distinct matches = 1:
+	96.1
+
+
+9,netssh.c:2269,2271, # distinct matches = 1:
+	326.1
+
+
+9,netssh.c:2310,2310, # distinct matches = 1:
+	107.1
+
+9,netssh.c:2311,2314, # distinct matches = 4:
+	570.1, 568.1, 566.1, 107.1
+
+9,netssh.c:2315,2315, # distinct matches = 1:
+	107.1
+
+
+9,netssh.c:2319,2319, # distinct matches = 1:
+	107.2
+
+9,netssh.c:2320,2323, # distinct matches = 4:
+	569.1, 571.1, 567.1, 107.2
+
+9,netssh.c:2324,2324, # distinct matches = 2:
+	106.2, 107.2
+
+9,netssh.c:2325,2326, # distinct matches = 1:
+	106.2
+
+
+9,netssh.c:2351,2356, # distinct matches = 1:
+	71.1
+
+
+9,netssh.c:2359,2362, # distinct matches = 4:
+	351.1, 565.1, 569.2, 568.2
+
+9,netssh.c:2363,2363, # distinct matches = 1:
+	565.1
+
+
+9,netssh.c:2365,2367, # distinct matches = 1:
+	311.2
+
+9,netssh.c:2368,2371, # distinct matches = 4:
+	566.2, 572.1, 567.2, 565.2
+
+9,netssh.c:2372,2372, # distinct matches = 1:
+	565.2
+
+
+9,netssh.c:2378,2380, # distinct matches = 1:
+	367.2
+
+
+9,netssh.c:2387,2390, # distinct matches = 1:
+	349.2
+
+
+9,netssh.c:2397,2400, # distinct matches = 4:
+	572.2, 571.2, 570.2, 351.2
+
+
+9,netssh.c:2412,2417, # distinct matches = 1:
+	71.2
+
+
+9,netssh.c:2423,2423, # distinct matches = 1:
+	582.2
+
+9,netssh.c:2424,2424, # distinct matches = 5:
+	558.2, 552.1, 559.2, 555.2, 582.2
+
+9,netssh.c:2425,2428, # distinct matches = 9:
+	78.2, 74.2, 73.2, 81.2, 558.2, 552.1, 559.2, 555.2, 582.2
+
+
+9,netssh.c:2431,2433, # distinct matches = 1:
+	611.2
+
+
+9,netssh.c:2440,2444, # distinct matches = 1:
+	337.2
+
+
+9,netssh.c:2449,2451, # distinct matches = 1:
+	108.1
+
+
+9,netssh.c:2502,2507, # distinct matches = 1:
+	96.2
+
+
+9,netssh.c:2509,2511, # distinct matches = 1:
+	326.2
+
+
+9,netssh.c:2519,2524, # distinct matches = 2:
+	149.2, 165.2
+
+
+9,netssh.c:2538,2538, # distinct matches = 3:
+	472.2, 473.2, 471.2
+
+9,netssh.c:2539,2540, # distinct matches = 4:
+	103.2, 472.2, 473.2, 471.2
+
+9,netssh.c:2541,2542, # distinct matches = 2:
+	103.2, 471.2
+
+
+9,netssh.c:2554,2554, # distinct matches = 3:
+	486.1, 225.1, 224.2
+
+9,netssh.c:2555,2557, # distinct matches = 4:
+	508.2, 486.1, 225.1, 224.2
+
+9,netssh.c:2558,2559, # distinct matches = 1:
+	508.2
+
+
+9,netssh.c:2582,2594, # distinct matches = 1:
+	516.1
+
+9,netssh.c:2595,2595, # distinct matches = 6:
+	6.1, 271.1, 270.1, 273.1, 5.1, 274.1, 272.1, 516.1
+
+9,netssh.c:2596,2598, # distinct matches = 5:
+	6.1, 271.1, 270.1, 273.1, 5.1, 274.1, 272.1
+
+
+9,netssh.c:2601,2602, # distinct matches = 1:
+	413.1, 6.1, 5.1
+
+9,netssh.c:2603,2607, # distinct matches = 2:
+	590.1, 413.1, 6.1, 5.1
+
+9,netssh.c:2608,2608, # distinct matches = 3:
+	274.2, 590.1, 413.1, 6.1, 5.1
+
+9,netssh.c:2609,2611, # distinct matches = 2:
+	274.2, 413.1, 6.1, 5.1
+
+
+9,netssh.c:2614,2615, # distinct matches = 1:
+	212.1
+
+9,netssh.c:2616,2620, # distinct matches = 2:
+	587.1, 212.1
+
+9,netssh.c:2621,2621, # distinct matches = 3:
+	271.2, 5.2 (coincides with 271.2), 587.1, 212.1
+
+9,netssh.c:2622,2624, # distinct matches = 2:
+	271.2, 5.2 (coincides with 271.2), 212.1
+
+9,netssh.c:2625,2626, # distinct matches = 1:
+	5.2 (coincides with 271.2), 212.1
+
+9,netssh.c:2627,2628, # distinct matches = 2:
+	212.2, 5.2 (coincides with 271.2), 212.1
+
+9,netssh.c:2629,2633, # distinct matches = 3:
+	586.1, 212.2, 5.2 (coincides with 271.2), 212.1
+
+9,netssh.c:2634,2634, # distinct matches = 4:
+	270.2, 6.2 (coincides with 270.2), 586.1, 212.2, 5.2 (coincides with 271.2), 212.1
+
+9,netssh.c:2635,2637, # distinct matches = 3:
+	270.2, 6.2 (coincides with 270.2), 212.2, 5.2 (coincides with 271.2), 212.1
+
+9,netssh.c:2638,2639, # distinct matches = 1:
+	6.2 (coincides with 270.2), 212.2
+
+9,netssh.c:2640,2641, # distinct matches = 2:
+	412.1, 6.2 (coincides with 270.2), 212.2
+
+9,netssh.c:2642,2646, # distinct matches = 3:
+	588.1, 412.1, 6.2 (coincides with 270.2), 212.2
+
+9,netssh.c:2647,2647, # distinct matches = 4:
+	273.2, 588.1, 412.1, 6.2 (coincides with 270.2), 212.2
+
+9,netssh.c:2648,2650, # distinct matches = 3:
+	273.2, 412.1, 6.2 (coincides with 270.2), 212.2
+
+9,netssh.c:2651,2652, # distinct matches = 1:
+	412.1
+
+9,netssh.c:2653,2654, # distinct matches = 3:
+	412.2, 413.2, 412.1
+
+9,netssh.c:2655,2659, # distinct matches = 4:
+	589.1, 412.2, 413.2, 412.1
+
+9,netssh.c:2660,2660, # distinct matches = 5:
+	272.2, 589.1, 412.2, 413.2, 412.1
+
+9,netssh.c:2661,2663, # distinct matches = 3:
+	272.2, 412.2, 413.2
+
+9,netssh.c:2664,2667, # distinct matches = 1:
+	412.2
+
+9,netssh.c:2668,2672, # distinct matches = 2:
+	591.1, 412.2
+
+9,netssh.c:2673,2673, # distinct matches = 4:
+	13.1, 11.1, 591.1, 412.2
+
+9,netssh.c:2674,2675, # distinct matches = 5:
+	11.2, 12.1, 13.1, 11.1, 591.1
+
+9,netssh.c:2676,2676, # distinct matches = 3:
+	11.2, 12.1, 591.1
+
+9,netssh.c:2677,2677, # distinct matches = 1:
+	591.1
+
+
+9,netssh.c:2679,2679, # distinct matches = 1:
+	425.2
+
+9,netssh.c:2680,2680, # distinct matches = 2:
+	424.1, 425.2
+
+9,netssh.c:2681,2681, # distinct matches = 3:
+	516.2, 424.1, 425.2
+
+9,netssh.c:2682,2683, # distinct matches = 2:
+	516.2, 424.1
+
+9,netssh.c:2684,2692, # distinct matches = 1:
+	516.2
+
+9,netssh.c:2693,2693, # distinct matches = 5:
+	9.1, 10.1, 8.1, 7.1, 516.2
+
+9,netssh.c:2694,2695, # distinct matches = 4:
+	9.1, 10.1, 8.1, 7.1
+
+9,netssh.c:2696,2696, # distinct matches = 5:
+	445.1, 9.1, 10.1, 8.1, 7.1
+
+9,netssh.c:2697,2697, # distinct matches = 3:
+	445.1, 8.1, 7.1
+
+9,netssh.c:2698,2698, # distinct matches = 5:
+	411.1, 180.1, 407.1, 179.1, 445.1, 8.1, 7.1
+
+9,netssh.c:2699,2701, # distinct matches = 6:
+	590.2, 411.1, 180.1, 407.1, 179.1, 445.1, 8.1, 7.1
+
+9,netssh.c:2702,2704, # distinct matches = 5:
+	590.2, 411.1, 407.1, 445.1, 8.1, 7.1
+
+9,netssh.c:2705,2705, # distinct matches = 3:
+	407.1, 445.1, 8.1, 7.1
+
+
+9,netssh.c:2707,2708, # distinct matches = 3:
+	445.2, 444.1, 443.1
+
+9,netssh.c:2709,2709, # distinct matches = 5:
+	173.1, 410.1, 174.1, 406.1, 209.1, 445.2, 444.1, 443.1
+
+9,netssh.c:2710,2712, # distinct matches = 6:
+	587.2, 173.1, 410.1, 174.1, 406.1, 209.1, 445.2, 444.1, 443.1
+
+9,netssh.c:2713,2714, # distinct matches = 5:
+	587.2, 410.1, 406.1, 209.1, 445.2, 444.1, 443.1
+
+9,netssh.c:2715,2715, # distinct matches = 6:
+	8.2, 587.2, 410.1, 406.1, 209.1, 445.2, 444.1, 443.1
+
+9,netssh.c:2716,2716, # distinct matches = 5:
+	8.2, 406.1, 209.1, 445.2, 444.1, 443.1
+
+9,netssh.c:2717,2717, # distinct matches = 4:
+	8.2, 406.1, 209.1, 443.1
+
+9,netssh.c:2718,2718, # distinct matches = 5:
+	444.2, 8.2, 406.1, 209.1, 443.1
+
+9,netssh.c:2719,2719, # distinct matches = 3:
+	444.2, 8.2, 209.1
+
+9,netssh.c:2720,2720, # distinct matches = 5:
+	178.1, 209.2, 177.1, 409.1, 405.1, 210.1, 444.2 (coincides with 209.2), 8.2, 209.1
+
+9,netssh.c:2721,2723, # distinct matches = 6:
+	586.2, 178.1, 209.2, 177.1, 409.1, 405.1, 210.1, 444.2 (coincides with 209.2), 8.2, 209.1
+
+9,netssh.c:2724,2724, # distinct matches = 6:
+	586.2, 209.2, 409.1, 405.1, 210.1, 444.2 (coincides with 209.2), 8.2, 209.1
+
+9,netssh.c:2725,2725, # distinct matches = 7:
+	7.2, 586.2, 209.2, 409.1, 405.1, 210.1, 444.2 (coincides with 209.2), 8.2, 209.1
+
+9,netssh.c:2726,2726, # distinct matches = 5:
+	7.2, 209.2, 405.1, 210.1, 444.2 (coincides with 209.2), 8.2, 209.1
+
+9,netssh.c:2727,2727, # distinct matches = 4:
+	7.2, 209.2, 405.1, 210.1
+
+9,netssh.c:2728,2728, # distinct matches = 5:
+	443.2, 7.2, 209.2, 405.1, 210.1
+
+9,netssh.c:2729,2729, # distinct matches = 3:
+	443.2, 7.2, 209.2
+
+9,netssh.c:2730,2730, # distinct matches = 5:
+	210.2, 408.1, 175.1, 176.1, 404.1, 443.2, 7.2, 209.2 (coincides with 210.2)
+
+9,netssh.c:2731,2733, # distinct matches = 6:
+	588.2, 210.2, 408.1, 175.1, 176.1, 404.1, 443.2, 7.2, 209.2 (coincides with 210.2)
+
+9,netssh.c:2734,2735, # distinct matches = 5:
+	588.2, 210.2, 408.1, 404.1, 443.2, 7.2, 209.2 (coincides with 210.2)
+
+9,netssh.c:2736,2736, # distinct matches = 6:
+	10.2, 588.2, 210.2, 408.1, 404.1, 443.2, 7.2, 209.2 (coincides with 210.2)
+
+9,netssh.c:2737,2737, # distinct matches = 4:
+	10.2, 210.2, 404.1, 443.2, 7.2, 209.2 (coincides with 210.2)
+
+9,netssh.c:2738,2739, # distinct matches = 3:
+	10.2, 210.2, 404.1, 443.2
+
+
+9,netssh.c:2741,2741, # distinct matches = 4:
+	179.2, 404.2 (coincides with 175.2), 173.2 (coincides with 406.2), 177.2 (coincides with 405.2), 406.2, 407.2 (coincides with 179.2), 405.2, 175.2, 404.1
+
+9,netssh.c:2742,2744, # distinct matches = 5:
+	589.2, 179.2, 404.2 (coincides with 175.2), 173.2 (coincides with 406.2), 177.2 (coincides with 405.2), 406.2, 407.2 (coincides with 179.2), 405.2, 175.2, 404.1
+
+9,netssh.c:2745,2746, # distinct matches = 3:
+	589.2, 404.2 (coincides with 175.2), 406.2, 407.2 (coincides with 179.2), 405.2, 404.1
+
+9,netssh.c:2747,2747, # distinct matches = 4:
+	9.2, 589.2, 404.2 (coincides with 175.2), 406.2, 407.2 (coincides with 179.2), 405.2, 404.1
+
+9,netssh.c:2748,2748, # distinct matches = 3:
+	9.2, 404.2 (coincides with 175.2), 406.2, 407.2 (coincides with 179.2), 405.2
+
+9,netssh.c:2749,2750, # distinct matches = 3:
+	9.2, 404.2 (coincides with 175.2), 406.2, 405.2
+
+
+9,netssh.c:2752,2752, # distinct matches = 4:
+	178.2 (coincides with 409.2), 408.2, 180.2 (coincides with 411.2), 409.2, 410.2 (coincides with 174.2), 174.2, 411.2, 176.2 (coincides with 408.2), 404.2 (coincides with 175.2)
+
+9,netssh.c:2753,2755, # distinct matches = 5:
+	591.2, 178.2 (coincides with 409.2), 408.2, 180.2 (coincides with 411.2), 409.2, 410.2 (coincides with 174.2), 174.2, 411.2, 176.2 (coincides with 408.2), 404.2 (coincides with 175.2)
+
+9,netssh.c:2756,2757, # distinct matches = 4:
+	591.2, 408.2, 409.2, 410.2 (coincides with 174.2), 411.2, 404.2 (coincides with 175.2)
+
+9,netssh.c:2758,2758, # distinct matches = 6:
+	12.2, 14.1, 591.2, 408.2, 409.2, 410.2 (coincides with 174.2), 411.2, 404.2 (coincides with 175.2)
+
+9,netssh.c:2759,2760, # distinct matches = 5:
+	14.2, 13.2, 12.2, 14.1, 591.2
+
+9,netssh.c:2761,2761, # distinct matches = 3:
+	14.2, 13.2, 591.2
+
+9,netssh.c:2762,2762, # distinct matches = 1:
+	591.2
+
+
+9,netssh.c:2773,2779, # distinct matches = 1:
+	299.1
+
+
+9,netssh.c:2876,2879, # distinct matches = 1:
+	361.1
+
+9,netssh.c:2880,2882, # distinct matches = 2:
+	190.1, 361.1
+
+
+9,netssh.c:2890,2893, # distinct matches = 1:
+	361.2
+
+9,netssh.c:2894,2896, # distinct matches = 2:
+	189.1, 361.2
+
+
+9,netssh.c:2911,2913, # distinct matches = 2:
+	190.2, 189.2
+
+
+9,netssh.c:2920,2923, # distinct matches = 1:
+	424.2
+
+
+9,netssh.c:3073,3077, # distinct matches = 1:
+	299.2
+
+
+9,netssh.c:3160,3163, # distinct matches = 3:
+	20.2, 19.2, 23.1
+
+9,netssh.c:3164,3167, # distinct matches = 3:
+	23.2, 24.2, 19.2
+
+
+9,netssh.c:3185,3187, # distinct matches = 1:
+	28.1
+
+9,netssh.c:3188,3190, # distinct matches = 2:
+	245.2, 28.1
+
+9,netssh.c:3191,3193, # distinct matches = 1:
+	28.2
+
+9,netssh.c:3194,3196, # distinct matches = 2:
+	244.2, 28.2
+
+
+10,pubkey.c:35,36, # distinct matches = 1:
+	201.1
+
+10,pubkey.c:37,38, # distinct matches = 4:
+	316.1, 315.1, 434.2, 201.1
+
+10,pubkey.c:39,39, # distinct matches = 3:
+	316.1, 315.1, 434.2
+
+
+10,pubkey.c:49,52, # distinct matches = 1:
+	201.2
+
+
+10,pubkey.c:182,185, # distinct matches = 1:
+	166.1
+
+
+10,pubkey.c:195,198, # distinct matches = 1:
+	166.2
+
+
+10,pubkey.c:217,219, # distinct matches = 3:
+	317.1, 432.2, 316.2
+
+
+11,rsa2ssh2.c:1,6, # distinct matches = 6:
+	129.2, 131.2, 137.2, 128.2, 123.1, 130.2
+
+11,rsa2ssh2.c:7,7, # distinct matches = 5:
+	129.2, 131.2, 128.2, 123.1, 130.2
+
+11,rsa2ssh2.c:8,8, # distinct matches = 1:
+	123.1
+
+
+11,rsa2ssh2.c:15,17, # distinct matches = 3:
+	365.1, 364.1, 25.2
+
+11,rsa2ssh2.c:18,19, # distinct matches = 1:
+	25.2
+
+11,rsa2ssh2.c:20,24, # distinct matches = 3:
+	240.1, 513.1, 25.2
+
+
+12,ssh.c:24,26, # distinct matches = 3:
+	363.1, 365.2, 26.2
+
+12,ssh.c:27,29, # distinct matches = 1:
+	363.1
+
+12,ssh.c:30,30, # distinct matches = 3:
+	336.1, 517.1, 363.1
+
+12,ssh.c:31,38, # distinct matches = 2:
+	336.1, 517.1
+
+
+12,ssh.c:71,75, # distinct matches = 1:
+	616.1
+
+
+12,ssh.c:80,84, # distinct matches = 2:
+	518.1, 336.2
+
+
+12,ssh.c:112,116, # distinct matches = 6:
+	228.1, 230.1, 231.2, 495.2, 489.1, 227.2
+
+
+12,ssh.c:148,152, # distinct matches = 2:
+	517.2, 518.2
+
+
+12,ssh.c:163,165, # distinct matches = 1:
+	153.1
+
+
+12,ssh.c:167,171, # distinct matches = 1:
+	153.2
+
+
+12,ssh.c:218,218, # distinct matches = 7:
+	255.2, 254.2, 251.2, 578.2, 250.2, 248.2, 247.2
+
+12,ssh.c:219,219, # distinct matches = 8:
+	475.1, 255.2, 254.2, 251.2, 578.2, 250.2, 248.2, 247.2
+
+12,ssh.c:220,220, # distinct matches = 14:
+	492.2, 491.2, 490.1, 489.2, 487.2, 488.1, 475.1, 255.2, 254.2, 251.2, 578.2, 250.2, 248.2, 247.2
+
+12,ssh.c:221,221, # distinct matches = 7:
+	492.2, 491.2, 490.1, 489.2, 487.2, 488.1, 475.1
+
+12,ssh.c:222,223, # distinct matches = 6:
+	492.2, 491.2, 490.1, 489.2, 487.2, 488.1
+
+
+12,ssh.c:245,247, # distinct matches = 1:
+	18.1
+
+
+12,ssh.c:255,257, # distinct matches = 1:
+	366.1
+
+
+12,ssh.c:259,259, # distinct matches = 4:
+	228.2, 488.2, 226.2, 496.2
+
+12,ssh.c:260,263, # distinct matches = 5:
+	519.1, 228.2, 488.2, 226.2, 496.2
+
+12,ssh.c:264,264, # distinct matches = 1:
+	519.1
+
+12,ssh.c:265,265, # distinct matches = 2:
+	222.2, 519.1
+
+12,ssh.c:266,267, # distinct matches = 1:
+	222.2
+
+
+12,ssh.c:287,289, # distinct matches = 1:
+	18.2
+
+
+12,ssh.c:296,299, # distinct matches = 1:
+	366.2
+
+
+12,ssh.c:309,311, # distinct matches = 3:
+	234.2, 233.2, 499.2
+
+
+12,ssh.c:314,322, # distinct matches = 1:
+	519.2
+
+
+12,ssh.c:369,373, # distinct matches = 1:
+	581.2
+
+
+12,ssh.c:388,392, # distinct matches = 1:
+	616.2
+
+
+12,ssh.c:398,401, # distinct matches = 1:
+	62.1
+
+
+12,ssh.c:417,420, # distinct matches = 1:
+	62.2
+
+
+12,ssh.c:448,448, # distinct matches = 1:
+	475.2
+
+12,ssh.c:449,449, # distinct matches = 5:
+	485.2, 486.2, 481.1, 483.2, 475.2
+
+12,ssh.c:450,450, # distinct matches = 7:
+	513.2, 514.2, 485.2, 486.2, 481.1, 483.2, 475.2
+
+12,ssh.c:451,452, # distinct matches = 6:
+	513.2, 514.2, 485.2, 486.2, 481.1, 483.2
+
+12,ssh.c:453,454, # distinct matches = 2:
+	513.2, 514.2
+
+
+12,ssh.c:466,466, # distinct matches = 1:
+	352.2
+
+12,ssh.c:467,470, # distinct matches = 2:
+	145.1, 352.2
+
+
+12,ssh.c:496,498, # distinct matches = 2:
+	564.2, 563.1
+
+
+13,sshsession.c:26,28, # distinct matches = 3:
+	363.2, 364.2, 27.2
+
+13,sshsession.c:29,32, # distinct matches = 1:
+	363.2
+
+
+13,sshsession.c:59,66, # distinct matches = 1:
+	90.1
+
+
+13,sshsession.c:79,81, # distinct matches = 1:
+	260.1
+
+
+13,sshsession.c:85,87, # distinct matches = 3:
+	260.2, 258.1, 259.1
+
+13,sshsession.c:88,88, # distinct matches = 2:
+	258.1, 259.1
+
+13,sshsession.c:89,89, # distinct matches = 1:
+	258.1
+
+
+13,sshsession.c:105,112, # distinct matches = 1:
+	90.2
+
+
+13,sshsession.c:117,120, # distinct matches = 3:
+	477.2, 476.2, 223.2
+
+
+13,sshsession.c:146,146, # distinct matches = 5:
+	553.2, 345.2, 344.2, 552.2, 346.2
+
+13,sshsession.c:147,150, # distinct matches = 9:
+	80.2, 75.2, 76.2, 77.2, 553.2, 345.2, 344.2, 552.2, 346.2
+
+
+13,sshsession.c:171,175, # distinct matches = 1:
+	258.2
+
+
+13,sshsession.c:178,182, # distinct matches = 2:
+	239.2, 240.2
+
+
+13,sshsession.c:191,194, # distinct matches = 2:
+	145.2, 144.2
+
+
+13,sshsession.c:208,214, # distinct matches = 1:
+	151.2
+
+13,sshsession.c:215,217, # distinct matches = 2:
+	563.2, 151.2
+
+13,sshsession.c:218,219, # distinct matches = 1:
+	151.2
+
+
+13,sshsession.c:247,250, # distinct matches = 1:
+	259.2
+
+
+13,sshsession.c:302,305, # distinct matches = 4:
+	490.2, 230.2, 229.2, 494.2
+
+
+13,sshsession.c:328,331, # distinct matches = 3:
+	484.2, 225.2, 481.2
+
+
+13,sshsession.c:365,368, # distinct matches = 1:
+	185.1
+
+
+13,sshsession.c:373,376, # distinct matches = 1:
+	185.2
+
+
+13,sshsession.c:398,402, # distinct matches = 1:
+	501.1
+
+
+13,sshsession.c:409,410, # distinct matches = 1:
+	442.1
+
+13,sshsession.c:411,411, # distinct matches = 2:
+	501.2, 442.1
+
+13,sshsession.c:412,415, # distinct matches = 1:
+	501.2
+
+
+13,sshsession.c:438,438, # distinct matches = 1:
+	442.2
+
+13,sshsession.c:439,440, # distinct matches = 2:
+	423.1, 442.2
+
+13,sshsession.c:441,442, # distinct matches = 1:
+	423.1
+
+
+14,transport.c:1,6, # distinct matches = 6:
+	126.2, 125.2, 124.2, 123.2, 136.2, 127.2
+
+14,transport.c:7,7, # distinct matches = 5:
+	126.2, 125.2, 124.2, 123.2, 127.2
+
+14,transport.c:8,8, # distinct matches = 1:
+	123.2
+
+
+14,transport.c:22,26, # distinct matches = 1:
+	512.1
+
+
+14,transport.c:74,76, # distinct matches = 1:
+	169.1
+
+
+14,transport.c:105,108, # distinct matches = 1:
+	423.2
+
+
+14,transport.c:110,112, # distinct matches = 1:
+	169.2
+
+
+14,transport.c:129,134, # distinct matches = 1:
+	187.1
+
+
+14,transport.c:174,183, # distinct matches = 1:
+	187.2
+
+
+14,transport.c:189,191, # distinct matches = 1:
+	108.2
+
+
+14,transport.c:207,209, # distinct matches = 3:
+	315.2, 433.2, 317.2
+
+
+14,transport.c:214,218, # distinct matches = 1:
+	512.2
+

+ 36 - 0
sys/src/cmd/ssh2/dup.sum

@@ -0,0 +1,36 @@
+
+Summary:
+Reporting parameterized matches of at least 3 lines.
+15 files
+7163 lines in original code,
+6207 lines after pruning white space and comments.
+
+2195 lines were involved in 616 matches
+These constitute 35 percent of the ncsl.
+Approximately 1383 lines are redundant,or 22 percent of the ncsl.
+
+match length: number of matches of that length
+	   3: 266
+	   4: 186
+	   5:  36
+	   6:  47
+	   7:  22
+	   8:  13
+	   9:  13
+	  10:   5
+	  11:   5
+	  12:   3
+	  14:   4
+	  15:   1
+	  16:   4
+	  17:   1
+	  19:   2
+	  21:   1
+	  22:   1
+	  28:   1
+	  30:   1
+	  33:   1
+	  35:   1
+	  42:   1
+	  44:   1
+

+ 235 - 0
sys/src/cmd/ssh2/dup.xpr

@@ -0,0 +1,235 @@
+percent redundancy, # redundant lines, #matches; file1, file2
+percent of file involved, #lines in file involved; file1,filename1
+percent of file involved, #lines in file involved; file2,filename2
+
+13, 5, 1; 0,0
+      26, 11; 0,cipher3des.c:
+      26, 11; 0,cipher3des.c:
+
+36, 28, 5; 1,1
+      61, 48; 1,cipheraes.c:
+      61, 48; 1,cipheraes.c:
+
+7, 4, 1; 2,2
+      13, 8; 2,cipherblowfish.c:
+      13, 8; 2,cipherblowfish.c:
+
+15, 5, 1; 3,3
+      28, 11; 3,cipherrc4.c:
+      28, 11; 3,cipherrc4.c:
+
+19, 152, 60; 5,5
+      30, 244; 5,dh.c:
+      30, 244; 5,dh.c:
+
+7, 28, 9; 6,6
+      13, 56; 6,dial.c:
+      13, 56; 6,dial.c:
+
+4, 17, 5; 7,7
+      8, 34; 7,dial.thread.c:
+      8, 34; 7,dial.thread.c:
+
+22, 641, 316; 9,9
+      34, 992; 9,netssh.c:
+      34, 992; 9,netssh.c:
+
+5, 10, 3; 10,10
+      10, 20; 10,pubkey.c:
+      10, 20; 10,pubkey.c:
+
+8, 37, 13; 12,12
+      14, 67; 12,ssh.c:
+      14, 67; 12,ssh.c:
+
+7, 28, 7; 13,13
+      13, 54; 13,sshsession.c:
+      13, 54; 13,sshsession.c:
+
+6, 12, 3; 14,14
+      11, 24; 14,transport.c:
+      11, 24; 14,transport.c:
+
+19, 22, 6; 0,1
+      47, 20; 0,cipher3des.c:
+      25, 20; 1,cipheraes.c:
+
+16, 22, 6; 1,2
+      25, 20; 1,cipheraes.c:
+      33, 20; 2,cipherblowfish.c:
+
+26, 25, 5; 2,3
+      38, 23; 2,cipherblowfish.c:
+      59, 23; 3,cipherrc4.c:
+
+1, 13, 6; 5,6
+      2, 15; 5,dh.c:
+      2, 9; 6,dial.c:
+
+29, 252, 33; 6,7
+      58, 247; 6,dial.c:
+      56, 239; 7,dial.thread.c:
+
+0, 10, 3; 8,9
+      40, 6; 8,esmprint.c:
+      0, 12; 9,netssh.c:
+
+0, 5, 2; 9,10
+      0, 3; 9,netssh.c:
+      3, 6; 10,pubkey.c:
+
+1, 7, 2; 11,12
+      12, 7; 11,rsa2ssh2.c:
+      1, 7; 12,ssh.c:
+
+2, 22, 6; 12,13
+      5, 23; 12,ssh.c:
+      5, 20; 13,sshsession.c:
+
+0, 3, 1; 13,14
+      1, 3; 13,sshsession.c:
+      1, 3; 14,transport.c:
+
+27, 27, 4; 0,2
+      60, 26; 0,cipher3des.c:
+      40, 24; 2,cipherblowfish.c:
+
+19, 22, 6; 1,3
+      25, 20; 1,cipheraes.c:
+      51, 20; 3,cipherrc4.c:
+
+0, 3, 1; 5,7
+      0, 3; 5,dh.c:
+      1, 3; 7,dial.thread.c:
+
+0, 15, 6; 7,9
+      3, 13; 7,dial.thread.c:
+      0, 14; 9,netssh.c:
+
+0, 9, 1; 9,11
+      0, 9; 9,netssh.c:
+      15, 9; 11,rsa2ssh2.c:
+
+1, 7, 2; 11,13
+      12, 7; 11,rsa2ssh2.c:
+      2, 7; 13,sshsession.c:
+
+32, 26, 5; 0,3
+      56, 24; 0,cipher3des.c:
+      62, 24; 3,cipherrc4.c:
+
+1, 21, 9; 6,9
+      4, 19; 6,dial.c:
+      1, 17; 9,netssh.c:
+
+2, 51, 22; 9,12
+      2, 58; 9,netssh.c:
+      8, 37; 12,ssh.c:
+
+3, 8, 1; 11,14
+      13, 8; 11,rsa2ssh2.c:
+      4, 8; 14,transport.c:
+
+1, 6, 1; 3,7
+      15, 6; 3,cipherrc4.c:
+      1, 6; 7,dial.thread.c:
+
+1, 33, 18; 5,9
+      3, 25; 5,dh.c:
+      1, 28; 9,netssh.c:
+
+1, 6, 1; 7,11
+      1, 6; 7,dial.thread.c:
+      10, 6; 11,rsa2ssh2.c:
+
+1, 40, 10; 9,13
+      1, 43; 9,netssh.c:
+      8, 36; 13,sshsession.c:
+
+1, 5, 2; 10,14
+      3, 6; 10,pubkey.c:
+      1, 3; 14,transport.c:
+
+1, 6, 1; 2,7
+      10, 6; 2,cipherblowfish.c:
+      1, 6; 7,dial.thread.c:
+
+1, 5, 2; 7,12
+      1, 3; 7,dial.thread.c:
+      1, 6; 12,ssh.c:
+
+0, 6, 2; 9,14
+      0, 6; 9,netssh.c:
+      3, 6; 14,transport.c:
+
+1, 4, 1; 0,6
+      9, 4; 0,cipher3des.c:
+      1, 4; 6,dial.c:
+
+1, 6, 1; 1,7
+      8, 6; 1,cipheraes.c:
+      1, 6; 7,dial.thread.c:
+
+1, 6, 3; 6,12
+      1, 3; 6,dial.c:
+      2, 9; 12,ssh.c:
+
+1, 9, 3; 7,13
+      2, 10; 7,dial.thread.c:
+      2, 7; 13,sshsession.c:
+
+2, 10, 2; 0,7
+      23, 10; 0,cipher3des.c:
+      2, 10; 7,dial.thread.c:
+
+2, 13, 5; 6,13
+      4, 16; 6,dial.c:
+      2, 10; 13,sshsession.c:
+
+1, 6, 1; 7,14
+      1, 6; 7,dial.thread.c:
+      3, 6; 14,transport.c:
+
+7, 7, 1; 3,11
+      18, 7; 3,cipherrc4.c:
+      12, 7; 11,rsa2ssh2.c:
+
+0, 3, 1; 5,13
+      0, 3; 5,dh.c:
+      1, 3; 13,sshsession.c:
+
+0, 6, 2; 0,9
+      9, 4; 0,cipher3des.c:
+      0, 8; 9,netssh.c:
+
+6, 7, 1; 2,11
+      12, 7; 2,cipherblowfish.c:
+      12, 7; 11,rsa2ssh2.c:
+
+5, 7, 1; 1,11
+      9, 7; 1,cipheraes.c:
+      12, 7; 11,rsa2ssh2.c:
+
+7, 7, 1; 0,11
+      16, 7; 0,cipher3des.c:
+      12, 7; 11,rsa2ssh2.c:
+
+3, 7, 1; 3,14
+      18, 7; 3,cipherrc4.c:
+      3, 7; 14,transport.c:
+
+3, 7, 1; 2,14
+      12, 7; 2,cipherblowfish.c:
+      3, 7; 14,transport.c:
+
+1, 4, 1; 0,13
+      9, 4; 0,cipher3des.c:
+      1, 4; 13,sshsession.c:
+
+2, 7, 1; 1,14
+      9, 7; 1,cipheraes.c:
+      3, 7; 14,transport.c:
+
+3, 7, 1; 0,14
+      16, 7; 0,cipher3des.c:
+      3, 7; 14,transport.c:

+ 17 - 0
sys/src/cmd/ssh2/esmprint.c

@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+esmprint(char *fmt, ...)
+{
+	va_list args;
+	char *p;
+
+	va_start(args, fmt);
+	p = vsmprint(fmt, args);
+	va_end(args);
+	if (p == nil)
+		sysfatal("esmprint: out of memory: %r");
+	setmalloctag(p, getcallerpc(&fmt));
+	return p;
+}

+ 39 - 0
sys/src/cmd/ssh2/funclen

@@ -0,0 +1,39 @@
+#!/bin/rc
+# funclen [file]... - print lengths of C functions in file(s)
+#	assumes the one true brace style (V7 kernel style).
+#	added some slight tolerance for bogus styles.
+exec awk '
+/^(#|\/\/)|;[\t ]*$|\\$|"\)|^[\t ]*\*/	{ next }
+/^((static|void|unsigned|int|u?v?long|double|char|struct[\t ]+[a-z_0-9]+)[\t ]*)*(\*[\t ]*)*[a-zA-Z0-9_µμ]+( +__P)? *\(/ {
+	# function name
+	paren = index($0, "(")
+	prelude = substr($0, 1, paren-1)
+	n = split(prelude, fields)
+	funcname = fields[n]
+}
+/^{/ {						# function or struct start
+	if (funcname == "")
+		next
+	if (start != 0)
+		print "unclosed function " funcname " at " FILENAME ":" FNR \
+			>"/fd/2"
+	start = FNR
+	file = FILENAME
+}
+/^}[^();]*($|\/\*|\/\/)/ && $0 !~ "^}[^*/]*[;={]" {
+	# function end, not struct end
+	if (start == 0 || file != FILENAME || funcname == "")
+		print "unopened function or macro end at " \
+			FILENAME ":" FNR >"/fd/2"
+	else
+		print FNR - start "\t" FILENAME ":" start "," FNR "\t" \
+			funcname "()"
+	start = 0				# function has ended
+	funcname = ""
+}
+END {
+	if (start != 0)
+		print "unclosed function " funcname " at " FILENAME ":" FNR \
+			>"/fd/2"
+}
+' $*

+ 79 - 0
sys/src/cmd/ssh2/long.funcs

@@ -0,0 +1,79 @@
+193	netssh.c:2233,2426	established()
+187	netssh.c:1066,1253	writectlproc()
+153	dh.c:51,204	dh_init()
+143	netssh.c:730,873	readreqrem()
+134	netssh.c:2078,2212	negotiating()
+125	netssh.c:601,726	stread()
+111	netssh.c:2444,2555	reader0()
+103	netssh.c:1257,1360	writereqremproc()
+98	sshsession.c:182,280	main()
+98	dh.c:453,551	dh_server()
+98	netssh.c:1899,1997	dohandshake()
+95	netssh.c:2586,2681	validatekexs()
+94	dh.c:808,902	genkeys()
+93	netssh.c:3117,3210	shutdown()
+92	dh.c:676,768	dh_client12()
+88	netssh.c:1408,1496	stclunk()
+85	dial.c:337,422	csdial()
+85	ssh.c:454,539	main()
+84	dial.thread.c:337,421	csdial()
+82	netssh.c:1538,1620	alloc_conn()
+81	netssh.c:396,477	stlisconn()
+79	netssh.c:2685,2764	validatekexc()
+76	dial.c:426,502	call()
+75	netssh.c:2999,3074	client_auth()
+74	netssh.c:988,1062	userauth()
+71	netssh.c:481,552	stlischan()
+70	netssh.c:2925,2995	auth_req()
+70	dial.thread.c:426,496	call()
+65	dh.c:306,371	rsa_verify()
+65	sshsession.c:333,398	newchannel()
+65	netssh.c:1624,1689	alloc_chan()
+65	netssh.c:317,382	stopen()
+64	netssh.c:2001,2065	send_kexinit()
+62	pubkey.c:142,204	replacekey()
+58	netssh.c:2863,2921	authreqpk()
+53	sshsession.c:444,497	runcmd()
+51	ssh.c:322,373	bidircopy()
+50	dh.c:247,297	rsa_sign()
+50	dh.c:622,672	verifyhostkey()
+49	ssh.c:265,314	keyproc()
+48	pubkey.c:17,65	parsepubkey()
+47	netssh.c:877,924	readdata()
+45	netssh.c:928,973	stwrite()
+45	netssh.c:168,213	threadmain()
+42	rsa2ssh2.c:24,66	main()
+41	netssh.c:556,597	getdata()
+41	transport.c:133,174	finish_packet()
+40	ssh.c:181,221	cmdmode()
+38	dh.c:391,429	dss_sign()
+38	dh.c:580,618	findkeyinuserring()
+35	dial.thread.c:93,128	dialimpl()
+35	ssh.c:225,260	keyprompt()
+35	dial.c:77,112	dialimpl()
+34	ssh.c:553,587	doauth()
+34	netssh.c:1364,1398	writedataproc()
+34	netssh.c:3079,3113	factlookup()
+32	dial.thread.c:243,275	recvresults()
+32	transport.c:182,214	undo_packet()
+32	netssh.c:2818,2850	keyfsauth()
+31	netssh.c:1772,1803	exchids()
+30	dial.c:303,333	dialmulti()
+30	dial.thread.c:284,314	dialmulti()
+30	netssh.c:2781,2811	mkcap()
+30	ssh.c:84,114	parseargs()
+29	netssh.c:273,302	server()
+29	dial.thread.c:199,228	callproc()
+28	dial.c:531,559	_dial_string_parse()
+28	dial.thread.c:525,553	_dial_string_parse()
+27	netssh.c:235,262	read9pmsg()
+27	netssh.c:1867,1894	dohandshake()
+27	cipherblowfish.c:16,43	initblowfish()
+27	netssh.c:1836,1863	deferredinit()
+26	sshsession.c:152,178	listenloop()
+26	pubkey.c:112,138	findkey()
+26	ssh.c:424,450	remotecmd()
+26	sshsession.c:122,148	authnewns()
+25	sshsession.c:415,440	confine()
+25	netssh.c:1500,1525	stflush()
+25	ssh.c:152,177	starttunnel()

+ 5 - 0
sys/src/cmd/ssh2/magic

@@ -0,0 +1,5 @@
+#!/bin/rc
+# print lines containing magic numbers
+g '[^0-9A-Za-z_]([2-9]|[0-9][0-9]+)[^0-9x]' |
+	grep -v '\.h:|#include|fprint\(2,|return (0|1|-1);' |
+	sed 's/:[	 ]+/: /'

+ 290 - 0
sys/src/cmd/ssh2/magic.out

@@ -0,0 +1,290 @@
+cipher3des.c:18: uchar key[3][8];
+cipher3des.c:44: "3des-cbc",
+cipher3des.c:45: 8,
+cipheraes.c:24: setupAESstate(&cs->state, c->s2cek, bits/8, c->s2civ);
+cipheraes.c:26: setupAESstate(&cs->state, c->c2sek, bits/8, c->c2siv);
+cipheraes.c:34: return initaes(c, dir, 128);
+cipheraes.c:40: return initaes(c, dir, 192);
+cipheraes.c:46: return initaes(c, dir, 256);
+cipherblowfish.c:22: for(i = 0; i < 16; ++i)
+cipherblowfish.c:25: for(i = 0; i < 16; ++i)
+cipherblowfish.c:28: for(i = 0; i < 8; ++i)
+cipherblowfish.c:31: for(i = 0; i < 8; ++i)
+cipherblowfish.c:39: setupBFstate(&cs->state, c->s2cek, 16, c->s2civ);
+cipherblowfish.c:41: setupBFstate(&cs->state, c->c2sek, 16, c->c2siv);
+cipherblowfish.c:62: 8,
+cipherrc4.c:21: setupRC4state(&cs->state, c->s2cek, 16);
+cipherrc4.c:23: setupRC4state(&cs->state, c->c2sek, 16);
+cipherrc4.c:41: 8,
+dh.c:21: "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+dh.c:28: * 2048-bit MODP group (id 14) from RFC 3526
+dh.c:32: "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+dh.c:37: "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+dh.c:38: "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+dh.c:41: "15728E5A8AACAA68FFFFFFFFFFFFFFFF";
+dh.c:60: two = strtomp("2", nil, 10, nil);
+dh.c:61: p1 = strtomp(group1p, nil, 16, nil);
+dh.c:62: p14 = strtomp(group14p, nil, 16, nil);
+dh.c:103: myrsakey.pub.n = strtomp(p+3, nil, 16, nil);
+dh.c:114: myrsakey.pub.ek = strtomp(p+4, nil, 16, nil);
+dh.c:127: myrsakey.dk = strtomp(p+5, nil, 16, nil);
+dh.c:170: mydsskey.pub.p = strtomp(p+3, nil, 16, nil);
+dh.c:177: mydsskey.pub.q = strtomp(p+3, nil, 16, nil);
+dh.c:184: mydsskey.pub.alpha = strtomp(p+7, nil, 16, nil);
+dh.c:191: mydsskey.pub.key = strtomp(p+5, nil, 16, nil);
+dh.c:202: mydsskey.secret = strtomp(p+9, nil, 16, nil);
+dh.c:233: n = nb - (nelem(sha1der) + SHA1dlen) - 3;
+dh.c:257: n = (nbit + 7) / 8;
+dh.c:260: /* Compute s: RFC 3447 */
+dh.c:276: auth_rpc(ar, "start", "role=sign proto=rsa", 19) != ARok ||
+dh.c:319: n = (nbit + 7) / 8;
+dh.c:321: /* Compute s: RFC 3447 */
+dh.c:324: s = mpnew(1024);
+dh.c:341: buf = emalloc9p(Blobsz / 2);
+dh.c:343: p = (char *)get_string(nil, (uchar *)sig, buf, Blobsz / 2, nil);
+dh.c:397: uchar sstr[2*SHA1dlen];
+dh.c:415: auth_rpc(ar, "start", "role=sign proto=dsa", 19) != ARok ||
+dh.c:427: add_block(sig, sstr, 2*SHA1dlen);
+dh.c:442: return dh_server(c, pack1, p1, 1024);
+dh.c:448: return dh_server(c, pack1, p14, 2048);
+dh.c:465: y = mprand(nbit / 8, genrandom, nil);
+dh.c:482: /* Compute H: RFC 4253 */
+dh.c:561: c->e = mpnew(1024);
+dh.c:566: c->x = mprand(128, genrandom, nil);
+dh.c:588: keymbox.msg = smprint("b%04ld%s", strlen(newkey), newkey);
+dh.c:597: keymbox.msg = smprint("%c%04ld%s", n == NoKeyFile || n == NoKey?
+dh.c:607: keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey);
+dh.c:633: return -2;
+dh.c:679: char buf[10];
+dh.c:694: q += nhgetl(q) + 4;
+dh.c:697: k = mpnew(1024);
+dh.c:700: /* Compute H: RFC 4253 */
+dh.c:731: q += nhgetl(q) + 4;
+dh.c:743: case -2:
+dh.c:747: keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey);
+dh.c:751: keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey);
+dh.c:778: e = mpnew(2048);
+dh.c:779: x = mprand(256, genrandom, nil);
+dh.c:810: char buf[82], *bp, *be;			/* magic 82 */
+dh.c:814: /* Compute 40 bytes (320 bits) of keys: each alg can use what it needs */
+dh.c:882: for (n = 0, bp = buf; n < SHA1dlen*2; ++n)
+dh.c:887: for (n = 0, bp = buf; n < SHA1dlen*2; ++n)
+dh.c:892: for (n = 0, bp = buf; n < SHA1dlen*2; ++n)
+dh.c:897: for (n = 0, bp = buf; n < SHA1dlen*2; ++n)
+dial.c:14: Maxstring	= 128,
+dial.c:15: Maxpath		= 256,
+dial.c:17: Maxcsreply	= 64*80,	/* this is probably overly generous */
+dial.c:22: Maxconnms	= 2*60*1000,	/* 2 minutes */
+dial.c:171: char *fields[5];			/* pid + 3 times + error */
+dial.c:177: if (n < 4)
+dial.c:188: strncpy(conn->err, fields[4], sizeof conn->err);
+dial.c:209: char exitsts[2*ERRMAX];
+dial.c:384: bleft = sizeof dp->addrlist - 2;	/* 2 is room for \n\0 */
+dial.thread.c:22: Defstksize	= 8192,
+dial.thread.c:24: Maxstring	= 128,
+dial.thread.c:25: Maxpath		= 256,
+dial.thread.c:27: Maxcsreply	= 64*80,	/* this is probably overly generous */
+dial.thread.c:32: Maxconnms	= 2*60*1000,	/* 2 minutes */
+dial.thread.c:57: int	kidthrids[64];		/* one per addr; ought to be enough */
+dial.thread.c:387: bleft = sizeof dp->addrlist - 2;	/* 2 is room for \n\0 */
+netssh.c:48: PKA *pkas[3];
+netssh.c:71: char uid[32];
+netssh.c:173: case '9':
+netssh.c:205: keymbox.mchan = chancreate(4, 0);
+netssh.c:254: werrstr("bad length in 9P2000 message header");
+netssh.c:278: netsshsrv.tree = alloctree(uid, uid, 0777, nil);
+netssh.c:279: rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR,
+netssh.c:281: clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone);
+netssh.c:282: ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl);
+netssh.c:283: keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem);
+netssh.c:296: d.mode = 0666;
+netssh.c:321: char buf[32];
+netssh.c:338: /* should use dial(2) instead of diddling /net/tcp */
+netssh.c:446: /* should use dial(2) instead of diddling /net/tcp */
+netssh.c:532: hnputl(p2->payload + 5, c->chans[i]->id);
+netssh.c:533: hnputl(p2->payload + 9, Maxpayload);
+netssh.c:534: hnputl(p2->payload + 13, Maxrpcbuf);
+netssh.c:535: p2->rlength = 18;
+netssh.c:582: if (sc->rwindow < 16*1024) {		/* magic.  half-way, maybe? */
+netssh.c:589: hnputl(p->payload+5, Maxpayload);
+netssh.c:590: p->rlength += 8;
+netssh.c:682: n = pread(c->ctlfd, buf, 10, 0); // magic 10
+netssh.c:716: n = pread(c->ctlfd, buf, 10, 0); /* magic 10 */
+netssh.c:804: n = ioread(io, c->ctlfd, buf, 10); /* magic 10 */
+netssh.c:980: * should use dial(2) instead of doing it by hand.
+netssh.c:982: sshdebug(c, "tcp connect %s %s", toks[1], ntok > 3? toks[2]: "");
+netssh.c:983: return fprint(c->ctlfd, "connect %s %s", toks[1], ntok > 3? toks[2]: "");
+netssh.c:990: char *attrs[5];
+netssh.c:993: if (ntok < 3 || ntok > 4)
+netssh.c:999: c->user = estrdup9p(toks[2]);
+netssh.c:1002: if (ntok == 4 && strcmp(toks[1], "k") == 0) {
+netssh.c:1009: c->password = estrdup9p(toks[3]);
+netssh.c:1019: attrs[2] = smprint("user=%s", c->user);
+netssh.c:1020: attrs[3] = smprint("sys=%s", c->remote);
+netssh.c:1024: if (ntok == 3)
+netssh.c:1025: c->authkey = factlookup(4, 2, attrs);
+netssh.c:1027: attrs[4] = toks[3];
+netssh.c:1028: c->authkey = factlookup(5, 2, attrs);
+netssh.c:1030: free(attrs[2]);
+netssh.c:1031: free(attrs[3]);
+netssh.c:1071: char *tcpconn2, *buf, *toks[4];
+netssh.c:1102: if (ntok < 2)
+netssh.c:1109: if (ntok < 2)
+netssh.c:1112: * should use dial(2) instead of doing it by hand.
+netssh.c:1122: pkas[2] = nil;
+netssh.c:1152: /* should use dial(2) instead of diddling /net/tcp */
+netssh.c:1178: add_string(p, toks[2]);
+netssh.c:1185: fprint(c->ctlfd, "reject %s %s", buf, toks[2]);
+netssh.c:1262: char *cmd, *q, *buf, *toks[4];
+netssh.c:1277: if (r->ifcall.count <= 10)
+netssh.c:1278: buf = emalloc9p(10 + 1);
+netssh.c:1347: for (n = 2; n < ntok; ++n) {
+netssh.c:1386: p->rlength += 4;
+netssh.c:1403: * called clunk.  But if there are no other references, a 9P Tclunk
+netssh.c:1475: p->rlength += 4;
+netssh.c:1594: c->dir = createfile(rootfile, buf, uid, 0555|DMDIR,
+netssh.c:1596: c->clonefile = createfile(c->dir, "clone", uid, 0666,
+netssh.c:1598: c->ctlfile = createfile(c->dir, "ctl", uid, 0666,
+netssh.c:1600: c->datafile = createfile(c->dir, "data", uid, 0666,
+netssh.c:1602: c->listenfile = createfile(c->dir, "listen", uid, 0666,
+netssh.c:1604: c->localfile = createfile(c->dir, "local", uid, 0444,
+netssh.c:1606: c->remotefile = createfile(c->dir, "remote", uid, 0444,
+netssh.c:1608: c->statusfile = createfile(c->dir, "status", uid, 0444,
+netssh.c:1610: c->tcpfile = createfile(c->dir, "tcp", uid, 0444,
+netssh.c:1652: sc->dir = createfile(c->dir, buf, uid, 0555|DMDIR,
+netssh.c:1654: sc->ctl = createfile(sc->dir, "ctl", uid, 0666,
+netssh.c:1656: sc->data = createfile(sc->dir, "data", uid, 0666,
+netssh.c:1658: sc->listen = createfile(sc->dir, "listen", uid, 0666,
+netssh.c:1660: sc->request = createfile(sc->dir, "request", uid, 0666,
+netssh.c:1662: sc->status = createfile(sc->dir, "status", uid, 0444,
+netssh.c:1664: sc->tcp = createfile(sc->dir, "tcp", uid, 0444,
+netssh.c:1679: sc->inchan = chancreate(4, 0);
+netssh.c:1683: sc->reqchan = chancreate(4, 0);
+netssh.c:1783: if (n < 5)		/* can't be a valid SSH id string */
+netssh.c:1790: if (n < 5)		/* can't be a valid SSH id string */
+netssh.c:1846: 0 && c->role == Client && strncmp(remid, "SSH-2", 5) != 0 &&
+netssh.c:1847: strncmp(remid, "SSH-1.99", 8) != 0) {
+netssh.c:1944: if (n < 5) {		/* can't be a valid SSH id string */
+netssh.c:1952: strncmp(path, "SSH-2", 5) != 0 &&
+netssh.c:1953: strncmp(path, "SSH-1.99", 8) != 0) {
+netssh.c:1990: /* should use hangup in dial(2) instead of diddling /net/tcp */
+netssh.c:1993: iowrite(io, fd, "hangup", 6);
+netssh.c:2015: for (i = 0; i < 16; ++i)
+netssh.c:2018: add_packet(c->skexinit, buf, 16);		/* cookie */
+netssh.c:2054: memset(buf, 0, 5);
+netssh.c:2055: add_packet(c->skexinit, buf, 5);
+netssh.c:2085: get_string(p, p->payload + 5, buf, Arbbufsz, nil);
+netssh.c:2095: memmove(c->c2siv, c->nc2siv, SHA1dlen*2);
+netssh.c:2096: memmove(c->s2civ, c->ns2civ, SHA1dlen*2);
+netssh.c:2097: memmove(c->c2sek, c->nc2sek, SHA1dlen*2);
+netssh.c:2098: memmove(c->s2cek, c->ns2cek, SHA1dlen*2);
+netssh.c:2099: memmove(c->c2sik, c->nc2sik, SHA1dlen*2);
+netssh.c:2100: memmove(c->s2cik, c->ns2cik, SHA1dlen*2);
+netssh.c:2222: add_block(p2, p->payload + 5, 4);
+netssh.c:2223: hnputl(p2->payload + p2->rlength - 1, 4);
+netssh.c:2224: p2->rlength += 4;
+netssh.c:2247: get_string(p, p->payload + 5, buf, Arbbufsz, nil);
+netssh.c:2256: get_string(p, p->payload + 2, buf, Arbbufsz, nil);
+netssh.c:2290: ch->twindow = nhgetl(q+4);
+netssh.c:2316: ch->otherid = nhgetl(p->payload+5);
+netssh.c:2317: ch->twindow = nhgetl(p->payload+9);
+netssh.c:2331: ch->twindow += nhgetl(p->payload + 5);
+netssh.c:2345: pl->rem = nhgetl(p->payload + 5);
+netssh.c:2346: pl->st = pl->pack->payload + 9;
+netssh.c:2348: pl->rem = nhgetl(p->payload + 9);
+netssh.c:2349: pl->st = pl->pack->payload + 13;
+netssh.c:2377: p2->rlength += 4;
+netssh.c:2402: q = get_string(p, p->payload+5, buf, Arbbufsz, nil);
+netssh.c:2409: memmove(pl->pack->payload + n, q + 1, p->rlength - (11 + (n-2)));
+netssh.c:2410: pl->rem = p->rlength - 11 + 2;
+netssh.c:2449: nb = 4;
+netssh.c:2468: np = ioreadn(c->rio, c->datafd, p->nlength + nb, p->rlength + 4 - nb);
+netssh.c:2470: nm = ioreadn(c->rio, c->datafd, p->nlength + p->rlength + 4,
+netssh.c:2471: SHA1dlen);		/* SHA1dlen was magic 20 */
+netssh.c:2485: /* SHA1dlen was magic 20 */
+netssh.c:2486: if (np != p->rlength + 4 - nb || c->inmac != -1 && nm != SHA1dlen) {
+netssh.c:2491: p->rlength = n - 4;
+netssh.c:2522: get_string(p, p->payload + 5, buf, Arbbufsz, nil);
+netssh.c:2533: case -2:
+netssh.c:2593: q = p->payload + 17;
+netssh.c:2692: q = p->payload + 17;
+netssh.c:2792: cap = emalloc9p(fromtosz + sizeof(rand)*3 + 1);
+netssh.c:2793: snprint(cap, fromtosz + sizeof(rand)*3 + 1, "%s@%s", from, to);
+netssh.c:2796: enc64(key, sizeof(rand)*3, rand, sizeof(rand));
+netssh.c:2839: werrstr("Password mismatch 2");
+netssh.c:2865: char method[32];
+netssh.c:2928: char key1[DESKEYLEN], key2[DESKEYLEN], method[32];
+netssh.c:3032: ek = strtomp(r+4, nil, 16, nil);
+netssh.c:3033: nk = strtomp(s+3, nil, 16, nil);
+netssh.c:3124: if (c->clonefile->ref <= 2 && c->ctlfile->ref <= 2 &&
+netssh.c:3125: c->datafile->ref <= 2 && c->listenfile->ref <= 2 &&
+netssh.c:3126: c->localfile->ref <= 2 && c->remotefile->ref <= 2 &&
+netssh.c:3127: c->statusfile->ref <= 2)
+pubkey.c:12: Arbsz =	256,
+pubkey.c:22: n = strtoul(s, &p, 10);
+pubkey.c:33: n = strtoul(s, &p, 10);
+pubkey.c:79: if(parsepubkey(s, key, sp, 10) == 0 ||
+pubkey.c:80: parsepubkey(s, key, sp, 16) == 0)
+pubkey.c:164: Bprint(bw, "%s %d %.10M %.10M\n",
+pubkey.c:169: Bprint(bw, "%s %d %.10M %.10M\n", host, mpsignif(hostkey->n),
+pubkey.c:213: fd = create(keyfile, OWRITE, 0666);
+pubkey.c:220: if(seek(fd, 0, 2) >= 0 &&
+pubkey.c:221: fprint(fd, "%s %d %.10M %.10M\n", host, mpsignif(key->n),
+rsa2ssh2.c:54: e = strtomp(ep+4, nil, 16, nil);
+rsa2ssh2.c:55: n = strtomp(np+3, nil, 16, nil);
+rsa2ssh2.c:61: sysfatal("base-64 encoding failed\n");
+ssh.c:140: //		for (fd = 3; fd < 40; fd++)
+ssh.c:196: if (buf[n-1] == ('u' & 037))
+ssh.c:228: write(kconsfd, buf+5, n);
+ssh.c:234: write(kconsfd, buf+5, n);
+ssh.c:242: seek(keyfd, 0, 2);
+ssh.c:243: if (readn(keyfd, buf, 5) <= 0)
+ssh.c:245: buf[5] = 0;
+ssh.c:246: n = strtol(buf+1, nil, 10);
+ssh.c:247: n = readn(keyfd, buf+5, n);
+ssh.c:250: buf[n+5] = 0;
+ssh.c:255: fprint(kconsfd, "%s\n", buf+5);
+ssh.c:269: if (size < 6)
+ssh.c:284: n = read(keyfd, buf, 5);		/* reading /net/ssh/keys */
+ssh.c:287: buf[5] = 0;
+ssh.c:288: n = strtol(buf+1, nil, 10);
+ssh.c:289: n = readn(keyfd, buf+5, n);
+ssh.c:290: buf[n+5] = 0;
+ssh.c:296: fprint(kconsfd, "%s\n", buf+5);
+ssh.c:382: ds = netmkaddr(remote, dir, "22");		/* tcp port 22 is ssh */
+ssh.c:544: char buf[64];
+ssh.c:548: return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
+sshsession.c:223: errfd = 2;
+sshsession.c:406: q += 4;
+sshsession.c:420: q += 4;
+sshsession.c:470: dup(datafd, 2);
+transport.c:42: p->rlength += 4;
+transport.c:50: *data += 4;
+transport.c:68: p->rlength += 4;
+transport.c:77: uchar nn[4];
+transport.c:82: memmove(q, nn, 4);
+transport.c:83: memmove(q+4, s, n);
+transport.c:84: p->rlength += n + 4;
+transport.c:95: q += 4;
+transport.c:115: n = mptobe(x, q + 4, Maxpktpay - p->rlength + 1 - 4, nil);
+transport.c:116: if(q[4] & 0x80){
+transport.c:117: memmove(q + 5, q + 4, n);
+transport.c:118: q[4] = 0;
+transport.c:122: p->rlength += n + 4;
+transport.c:128: return betomp(q + 4, nhgetl(q), nil);
+transport.c:139: blklen = 8;
+transport.c:145: if(blklen < 8)
+transport.c:146: blklen = 8;
+transport.c:149: n2 = blklen - (n1 + 5) % blklen;
+transport.c:150: if(n2 < 4)
+transport.c:162: memmove(buf + 4, p->nlength, p->rlength + 4);
+transport.c:163: hmac_sha1(buf, p->rlength + 8, c->outik, maclen, q, nil);
+transport.c:167: cryptos[c->encrypt]->encrypt(c->enccs, p->nlength, p->rlength + 4);
+transport.c:173: return p->rlength + 4 + maclen;
+transport.c:189: nb = 4;
+transport.c:193: p->rlength -= SHA1dlen;			/* was magic 20 */
+transport.c:197: p->rlength + 4 - nb);
+transport.c:201: memmove(buf + 4, p->nlength, nlength + 4);
+transport.c:202: hmac_sha1(buf, nlength + 8, c->inik, SHA1dlen, rmac, nil);
+transport.c:227: if(i % 16 == 15)
+transport.c:229: if(q - buf > Copybufsz - 4){

+ 75 - 0
sys/src/cmd/ssh2/mkfile

@@ -0,0 +1,75 @@
+</$objtype/mkfile
+
+TARG=\
+	netssh\
+	rsa2ssh2\
+	ssh\
+	sshsession\
+
+HFILES=\
+	netssh.h\
+	ssh2.h\
+
+CIPHEROFILES=\
+	cipher3des.$O\
+	cipherblowfish.$O\
+	cipherrc4.$O\
+	cipheraes.$O\
+
+FSOFILES=\
+	dh.$O\
+	pubkey.$O\
+	transport.$O\
+
+COMMON=\
+	esmprint.$O\
+	common.$O\
+
+CFILES=`{echo *.c}
+
+BIN=/$objtype/bin
+
+UPDATE=\
+	mkfile\
+	$HFILES\
+	${FSOFILES:%.$O=%.c}\
+	${CIPHEROFILES:%.$O=%.c}\
+	${TARG:%=%.c}\
+
+</sys/src/cmd/mkmany
+
+${TARG:%=$O.%}: $COMMON
+
+new:V:
+	rm -f /bin/sshsession /bin/aux/sshsession
+	mk install
+
+$O.rsa2ssh2 $O.ssh2key: transport.$O
+
+$O.ssh: dial.$O
+
+$O.netssh: $CIPHEROFILES $FSOFILES dial.thread.$O
+
+$BIN/sshsession:VQ: $BIN/aux/sshsession
+	;
+
+$BIN/aux/sshsession: $O.sshsession
+	cp $O.sshsession $BIN/aux/sshsession
+
+# $BIN/sshswitch:VQ: $BIN/aux/sshswitch
+# 	;
+#
+# $BIN/aux/sshswitch: $O.sshswitch
+# 	cp $O.sshswitch $BIN/aux/sshswitch
+
+analysis:V: magic.out long.funcs
+
+#dup:V: dup.out
+#dup.out dup.sum dup.xpr dup.pro:D: $HFILES $CFILES
+#	dup -t3 -xp *.c >dup.out
+
+magic.out:D: $HFILES $CFILES
+	magic >$target
+
+long.funcs:D: $CFILES
+	funclen *.c | sort -nr | awk '$1 > 24' >$target

+ 3210 - 0
sys/src/cmd/ssh2/netssh.c

@@ -0,0 +1,3210 @@
+/*
+ *  /net/ssh
+ */
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <mp.h>
+#include <auth.h>
+#include <authsrv.h>
+#include <libsec.h>
+#include <ip.h>
+#include "netssh.h"
+
+extern int nokeyverify;
+
+void stclunk(Fid *);
+void stend(Srv *);
+void stflush(Req *);
+void stopen(Req *);
+void stread(Req *);
+void stwrite(Req *);
+
+Srv netsshsrv = {
+	.open = stopen,
+	.read = stread,
+	.write = stwrite,
+	.flush = stflush,
+	.destroyfid = stclunk,
+	.end = stend,
+};
+
+Cipher *cryptos[] = {
+	&cipheraes128,
+	&cipheraes192,
+	&cipheraes256,
+//	&cipherblowfish,
+	&cipher3des,
+	&cipherrc4,
+};
+
+Kex *kexes[] = {
+	&dh1sha1,
+	&dh14sha1,
+};
+
+PKA *pkas[3];
+
+char *macnames[] = {
+	"hmac-sha1",
+};
+
+char *st_names[] = {
+[Empty]		"Empty",
+[Allocated]	"Allocated",
+[Initting]	"Initting",
+[Listening]	"Listening",
+[Opening]	"Opening",
+[Negotiating]	"Negotiating",
+[Authing]	"Authing",
+[Established]	"Established",
+[Eof]		"Eof",
+[Closing]	"Closing",
+[Closed]	"Closed",
+};
+
+int debug;
+int kflag;
+char *mntpt = "/net";
+char uid[32];
+Conn *connections[MAXCONN];
+File *rootfile, *clonefile, *ctlfile, *keysfile;
+Ioproc *io9p;
+MBox keymbox;
+QLock availlck;
+Rendez availrend;
+
+SSHChan *alloc_chan(Conn *);
+Conn *alloc_conn(void);
+int auth_req(Packet *, Conn *);
+int client_auth(Conn *, Ioproc *);
+int dohandshake(Conn *, char *);
+char *factlookup(int, int, char *[]);
+void filedup(Req *, File *);
+void readdata(void *);
+void reader(void *);
+void readreqrem(void *);
+void send_kexinit(Conn *);
+void server(char *, char *);
+void shutdown(Conn *);
+void stlisconn(void *);
+void stlischan(void *);
+int validatekex(Conn *, Packet *);
+int validatekexc(Packet *);
+int validatekexs(Packet *);
+void writectlproc(void *);
+void writedataproc(void *);
+void writereqremproc(void *);
+
+static int deferredinit(Conn *c);
+
+static void
+sshlogint(Conn *c, char *file, char *p)
+{
+	char *role, *id;
+
+	if (c == nil)
+		role = "";
+	else if (c->role == Server)
+		role = "server ";
+	else
+		role = "client ";
+	if (c == nil)
+		id = strdup("");
+	else if (c->user || c->remote)
+		id = smprint("user %s@%s id %d ", c->user, c->remote, c->id);
+	else
+		id = smprint("id %d ", c->id);
+
+	syslog(0, file, "%s: %s%s%s", argv0, role, id, p);
+	free(id);
+}
+
+void
+sshlog(Conn *c, char *fmt, ...)
+{
+	va_list args;
+	char *p;
+
+	/* do this first in case fmt contains "%r" */
+	va_start(args, fmt);
+	p = vsmprint(fmt, args);
+	va_end(args);
+
+	sshlogint(c, "ssh", p);
+	sshlogint(c, "sshdebug", p);	/* log in both places */
+	free(p);
+}
+
+void
+sshdebug(Conn *c, char *fmt, ...)
+{
+	va_list args;
+	char *p;
+
+	if (!debug)
+		return;
+
+	/* do this first in case fmt contains "%r" */
+	va_start(args, fmt);
+	p = vsmprint(fmt, args);
+	va_end(args);
+
+	sshlogint(c, "sshdebug", p);
+	free(p);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-dkv] [-m mntpt] [-s srvpt]\n", argv0);
+	exits("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+	char *p, *srvpt = nil;
+
+	threadsetname("main");
+	ARGBEGIN {
+	case '9':
+		chatty9p = 1;
+		break;
+	case 'd':
+		debug++;
+		break;
+	case 'k':
+		kflag = 1;
+		break;
+	case 'm':
+		mntpt = EARGF(usage());
+		break;
+	case 's':
+		srvpt = EARGF(usage());
+		break;
+	case 'v':
+		nokeyverify = 1;
+		break;
+	default:
+		usage();
+		break;
+	} ARGEND;
+
+	p = getenv("nosshkeyverify");
+	if (p) {
+		nokeyverify = 1;
+		free(p);
+	}
+
+	if (readfile("/dev/user", uid, sizeof uid) <= 0)
+		strcpy(uid, "none");
+
+	keymbox.mchan = chancreate(4, 0);
+	availrend.l = &availlck;
+	dh_init(pkas);
+
+	/* become a daemon */
+	if (rfork(RFNOTEG) < 0)
+		fprint(2, "%s: rfork(NOTEG) failed: %r\n", argv0);
+	server(mntpt, srvpt);
+}
+
+int
+readio(Ioproc *io, int fd, void *buf, int n)
+{
+	if (io)
+		return ioread(io, fd, buf, n);
+	else
+		return read(fd, buf, n);
+}
+
+int
+writeio(Ioproc *io, int fd, void *buf, int n)
+{
+	if (io)
+		return iowrite(io, fd, buf, n);
+	else
+		return write(fd, buf, n);
+}
+
+int
+read9pmsg(int fd, void *abuf, uint n)
+{
+	int m, len;
+	uchar *buf;
+
+	if (io9p == nil)
+		io9p = ioproc();
+
+	buf = abuf;
+
+	/* read count */
+	m = ioreadn(io9p, fd, buf, BIT32SZ);
+	if(m != BIT32SZ){
+		if(m < 0)
+			return -1;
+		return 0;
+	}
+
+	len = GBIT32(buf);
+	if(len <= BIT32SZ || len > n){
+		werrstr("bad length in 9P2000 message header");
+		return -1;
+	}
+	len -= BIT32SZ;
+	m = ioreadn(io9p, fd, buf+BIT32SZ, len);
+	if(m < len)
+		return 0;
+	return BIT32SZ+m;
+}
+
+void
+stend(Srv *)
+{
+	closeioproc(io9p);
+	threadkillgrp(threadgetgrp());
+}
+
+void
+server(char *mntpt, char *srvpt)
+{
+	Dir d;
+	char *p;
+	int fd;
+
+	netsshsrv.tree = alloctree(uid, uid, 0777, nil);
+	rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR,
+		(void*)Qroot);
+	clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone);
+	ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl);
+	keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem);
+
+	threadpostmountsrv(&netsshsrv, srvpt, mntpt, MAFTER);
+
+	p = esmprint("%s/cs", mntpt);
+	fd = open(p, OWRITE);
+	free(p);
+	if (fd >= 0) {
+		fprint(fd, "add ssh");
+		close(fd);
+	}
+	if (srvpt) {
+		nulldir(&d);
+		d.mode = 0666;
+		p = esmprint("/srv/%s", srvpt);
+		dirwstat(p, &d);
+		free(p);
+	}
+	sshdebug(nil, "server started for %s", getuser());
+}
+
+static void
+respexit(Conn *c, Req *r, void *freeme, char *msg)
+{
+	if (msg)
+		sshdebug(c, "%s", msg);
+	r->aux = 0;
+	respond(r, msg);
+	free(freeme);
+	threadexits(nil);	/* maybe use msg here */
+}
+
+void
+stopen(Req *r)
+{
+	int lev, xconn, fd;
+	uvlong qidpath;
+	char *p;
+	char buf[32];
+	Conn *c;
+	SSHChan *sc;
+
+	qidpath = (uvlong)r->fid->file->aux;
+	lev = qidpath >> Levshift;
+	switch ((ulong)(qidpath & Qtypemask)) {
+	default:
+		respond(r, nil);
+		break;
+	case Qlisten:
+		r->aux = (void *)threadcreate((lev == Connection?
+			stlisconn: stlischan), r, Defstk);
+		break;
+	case Qclone:
+		switch (lev) {
+		case Top:
+			/* should use dial(2) instead of diddling /net/tcp */
+			p = esmprint("%s/tcp/clone", mntpt);
+			fd = open(p, ORDWR);
+			if (fd < 0) {
+				sshdebug(nil, "stopen: open %s failed: %r", p);
+				free(p);
+				responderror(r);
+				return;
+			}
+			free(p);
+
+			c = alloc_conn();
+			if (c == nil) {
+				close(fd);
+				respond(r, "no more connections");
+				return;
+			}
+			c->ctlfd = fd;
+			c->poisoned = 0;
+			filedup(r, c->ctlfile);
+			sshlog(c, "new connection on fd %d", fd);
+			break;
+		case Connection:
+			xconn = (qidpath >> Connshift) & Connmask;
+			c = connections[xconn];
+			if (c == nil) {
+				respond(r, "bad connection");
+				return;
+			}
+			sc = alloc_chan(c);
+			if (sc == nil) {
+				respond(r, "no more channels");
+				return;
+			}
+			filedup(r, sc->ctl);
+			break;
+		default:
+			snprint(buf, sizeof buf, "bad level %d", lev);
+			readstr(r, buf);
+			break;
+		}
+		respond(r, nil);
+		break;
+	}
+}
+
+static void
+listerrexit(Req *r, Ioproc *io, Conn *cl)
+{
+	r->aux = 0;
+	responderror(r);
+	closeioproc(io);
+	shutdown(cl);
+	threadexits(nil);
+}
+
+void
+stlisconn(void *a)
+{
+	int xconn, fd, n;
+	uvlong qidpath;
+	char *msg;
+	char buf[Numbsz], path[NETPATHLEN];
+	Conn *c, *cl;
+	Ioproc *io;
+	Req *r;
+
+	threadsetname("stlisconn");
+	r = a;
+	qidpath = (uvlong)r->fid->file->aux;
+	xconn = (qidpath >> Connshift) & Connmask;
+
+	cl = connections[xconn];
+	if (cl == nil) {
+		sshlog(cl, "bad connection");
+		respond(r, "bad connection");
+		threadexits("bad connection");
+	}
+	if (cl->poisoned) {
+		sshdebug(cl, "stlisconn conn %d poisoned", xconn);
+		r->aux = 0;
+		respond(r, "top level listen conn poisoned");
+		threadexits("top level listen conn poisoned");
+	}
+	if (cl->ctlfd < 0) {
+		sshdebug(cl, "stlisconn conn %d ctlfd < 0; poisoned", xconn);
+		r->aux = 0;
+		respond(r, "top level listen with closed fd");
+		shutdown(cl);
+		cl->poisoned = 1;	/* no more use until ctlfd is set */
+		threadexits("top level listen with closed fd");
+	}
+
+	io = ioproc();
+
+	/* read xconn's tcp conn's ctl file */
+	seek(cl->ctlfd, 0, 0);
+	n = ioread(io, cl->ctlfd, buf, sizeof buf - 1);
+	if (n == 0) {
+		sshlog(cl, "stlisconn read eof on fd %d", cl->ctlfd);
+		listerrexit(r, io, cl);
+	} else if (n < 0) {
+		sshlog(cl, "stlisconn read failed on fd %d: %r", cl->ctlfd);
+		listerrexit(r, io, cl);
+	}
+	buf[n] = '\0';
+
+	cl->state = Listening;
+	/* should use dial(2) instead of diddling /net/tcp */
+	snprint(path, sizeof path, "%s/tcp/%s/listen", mntpt, buf);
+	for(;;) {
+		fd = ioopen(io, path, ORDWR);
+		if (fd < 0) 
+			listerrexit(r, io, cl);
+		c = alloc_conn();
+		if (c)
+			break;
+		n = ioread(io, fd, buf, sizeof buf - 1);
+		if (n <= 0)
+			listerrexit(r, io, cl);
+		buf[n] = '\0';
+		msg = smprint("reject %s no available connections", buf);
+		iowrite(io, fd, msg, strlen(msg));
+		free(msg);
+		close(fd);			/* surely ioclose? */
+	}
+	c->ctlfd = fd;
+	if (c->ctlfd < 0) {
+		sshlog(cl, "stlisconn c->ctlfd < 0 for conn %d", xconn);
+		threadexitsall("stlisconn c->ctlfd < 0");
+	}
+	c->poisoned = 0;
+	c->stifle = 1;			/* defer server; was for coexistence */
+	filedup(r, c->ctlfile);
+	sshdebug(c, "responding to listen open");
+	r->aux = 0;
+	respond(r, nil);
+	closeioproc(io);
+	threadexits(nil);
+}
+
+void
+stlischan(void *a)
+{
+	Req *r;
+	Packet *p2;
+	Ioproc *io;
+	Conn *c;
+	SSHChan *sc;
+	int i, n, xconn;
+	uvlong qidpath;
+
+	threadsetname("stlischan");
+	r = a;
+	qidpath = (uvlong)r->fid->file->aux;
+	xconn = (qidpath >> Connshift) & Connmask;
+	c = connections[xconn];
+	if (c == nil) {
+		respond(r, "bad channel");
+		sshlog(c, "bad channel");
+		threadexits(nil);
+	}
+	if (c->state == Closed || c->state == Closing)
+		respexit(c, r, nil, "channel listen on closed connection");
+	sc = c->chans[qidpath & Chanmask];
+
+	qlock(&c->l);
+	sc->lreq = r;
+	for (i = 0; i < c->nchan; ++i)
+		if (c->chans[i] && c->chans[i]->state == Opening &&
+		    c->chans[i]->ann && strcmp(c->chans[i]->ann, sc->ann) == 0)
+			break;
+	if (i >= c->nchan) {
+		sc->state = Listening;
+		rsleep(&sc->r);
+		i = sc->waker;
+		if (i < 0) {
+			qunlock(&c->l);
+			r->aux = 0;
+			responderror(r);
+			threadexits(nil);
+		}
+	} else
+		rwakeup(&c->chans[i]->r);
+	qunlock(&c->l);
+
+	if (c->state == Closed || c->state == Closing || c->state == Eof)
+		respexit(c, r, nil, "channel listen on closed connection");
+	c->chans[i]->state = Established;
+
+	p2 = new_packet(c);
+	c->chans[i]->rwindow = Maxpayload;
+	add_byte(p2, SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+	hnputl(p2->payload + 1, c->chans[i]->otherid);
+	hnputl(p2->payload + 5, c->chans[i]->id);
+	hnputl(p2->payload + 9, Maxpayload);
+	hnputl(p2->payload + 13, Maxrpcbuf);
+	p2->rlength = 18;
+	n = finish_packet(p2);
+	filedup(r, c->chans[i]->ctl);
+
+	io = ioproc();
+	n = iowrite(io, c->datafd, p2->nlength, n);
+	closeioproc(io);
+
+	free(p2);
+
+	sshdebug(c, "responding to chan listen open");
+	r->aux = 0;
+	if (n < 0)
+		responderror(r);
+	else
+		respond(r, nil);
+	threadexits(nil);
+}
+
+void
+getdata(Conn *c, SSHChan *sc, Req *r)
+{
+	Packet *p;
+	Plist *d;
+	int n;
+
+	n = r->ifcall.count;
+	if (sc->dataq->rem < n)
+		n = sc->dataq->rem;
+	if (n > Maxrpcbuf)
+		n = Maxrpcbuf;
+	r->ifcall.offset = 0;
+
+	readbuf(r, sc->dataq->st, n);
+	sc->dataq->st += n;
+	sc->dataq->rem -= n;
+	sc->inrqueue -= n;
+	if (sc->dataq->rem <= 0) {
+		d = sc->dataq;
+		sc->dataq = sc->dataq->next;
+		if (d->pack->tlength > sc->rwindow)
+			sc->rwindow = 0;
+		else
+			sc->rwindow -= d->pack->tlength;
+		free(d->pack);
+		free(d);
+	}
+	if (sc->rwindow < 16*1024) {		/* magic.  half-way, maybe? */
+		sc->rwindow += Maxpayload;
+		sshdebug(c, "increasing receive window to %lud, inq %lud\n",
+			argv0, sc->rwindow, sc->inrqueue);
+		p = new_packet(c);
+		add_byte(p, SSH_MSG_CHANNEL_WINDOW_ADJUST);
+		hnputl(p->payload+1, sc->otherid);
+		hnputl(p->payload+5, Maxpayload);
+		p->rlength += 8;
+		n = finish_packet(p);
+		iowrite(c->dio, c->datafd, p->nlength, n);
+		free(p);
+	}
+	r->aux = 0;
+	respond(r, nil);
+}
+
+void
+stread(Req *r)
+{
+	Conn *c;
+	SSHChan *sc;
+	int n, lev, cnum, xconn;
+	uvlong qidpath;
+	char buf[Arbbufsz], path[NETPATHLEN];
+
+	threadsetname("stread");
+	qidpath = (uvlong)r->fid->file->aux;
+	lev = qidpath >> Levshift;
+	xconn = (qidpath >> Connshift) & Connmask;
+	c = connections[xconn];
+	if (c == nil) {
+		if (lev != Top || (qidpath & Qtypemask) != Qreqrem) {
+			respond(r, "Invalid connection");
+			return;
+		}
+		cnum = 0;
+		sc = nil;
+	} else {
+		cnum = qidpath & Chanmask;
+		sc = c->chans[cnum];
+	}
+	switch ((ulong)(qidpath & Qtypemask)) {
+	case Qctl:
+	case Qlisten:
+		if (r->ifcall.offset != 0) {
+			respond(r, nil);
+			break;
+		}
+		switch (lev) {
+		case Top:
+			readstr(r, st_names[c->state]);
+			break;
+		case Connection:
+		case Subchannel:
+			snprint(buf, sizeof buf, "%d", lev == Connection?
+				xconn: cnum);
+			readstr(r, buf);
+			break;
+		default:
+			snprint(buf, sizeof buf, "stread error, level %d", lev);
+			respond(r, buf);
+			return;
+		}
+		respond(r, nil);
+		break;
+	case Qclone:
+		if (r->ifcall.offset != 0) {
+			respond(r, nil);
+			break;
+		}
+		readstr(r, "Congratulations, you've achieved the impossible\n");
+		respond(r, nil);
+		break;
+	case Qdata:
+		if (lev == Top) {
+			respond(r, nil);
+			break;
+		}
+		if (lev == Connection) {
+			if (0 && c->stifle) {	/* was for coexistence */
+				c->stifle = 0;
+				if (deferredinit(c) < 0) {
+					respond(r, "deferredinit failed");
+					break;
+				}
+			}
+			if (c->cap)			/* auth capability? */
+				readstr(r, c->cap);
+			respond(r, nil);
+			break;
+		}
+
+		r->aux = (void *)threadcreate(readdata, r, Defstk);
+		break;
+	case Qlocal:
+		if (lev == Connection)
+			if (c->ctlfd < 0)
+				readstr(r, "::!0\n");
+			else {
+				n = pread(c->ctlfd, buf, 10, 0); // magic 10
+				buf[n >= 0? n: 0] = '\0';
+				snprint(path, sizeof path, "%s/tcp/%s/local",
+					mntpt, buf);
+				readfile(path, buf, sizeof buf);
+				readstr(r, buf);
+			}
+		respond(r, nil);
+		break;
+	case Qreqrem:
+		r->aux = (void *)threadcreate(readreqrem, r, Defstk);
+		break;
+	case Qstatus:
+		switch (lev) {
+		case Top:
+			readstr(r, "Impossible");
+			break;
+		case Connection:
+			readstr(r, (uint)c->state > Closed?
+				"Unknown": st_names[c->state]);
+			break;
+		case Subchannel:
+			readstr(r, (uint)sc->state > Closed?
+				"Unknown": st_names[sc->state]);
+			break;
+		}
+		respond(r, nil);
+		break;
+	case Qtcp:
+		/* connection number of underlying tcp connection */
+		if (lev == Connection)
+			if (c->ctlfd < 0)
+				readstr(r, "-1\n");
+			else {
+				n = pread(c->ctlfd, buf, 10, 0); /* magic 10 */
+				buf[n >= 0? n: 0] = '\0';
+				readstr(r, buf);
+			}
+		respond(r, nil);
+		break;
+	default:
+		respond(r, nil);
+		break;
+	}
+}
+
+void
+readreqrem(void *a)
+{
+	Ioproc *io;
+	Req *r;
+	Conn *c;
+	SSHChan *sc;
+	int fd, n, lev, cnum, xconn;
+	uvlong qidpath;
+	char buf[Arbbufsz], path[NETPATHLEN];
+
+	threadsetname("readreqrem");
+	r = a;
+	qidpath = (uvlong)r->fid->file->aux;
+	lev = qidpath >> Levshift;
+	xconn = (qidpath >> Connshift) & Connmask;
+	c = connections[xconn];
+	if (c == nil) {
+		if (lev != Top) {
+			respond(r, "Invalid connection");
+			return;
+		}
+		sc = nil;
+	} else {
+		cnum = qidpath & Chanmask;
+		sc = c->chans[cnum];
+	}
+	switch (lev) {
+	case Top:
+		if (r->ifcall.offset == 0 && keymbox.state != Empty) {
+			r->aux = 0;
+			respond(r, "Key file collision");	/* WTF? */
+			break;
+		}
+		if (r->ifcall.offset != 0) {
+			readstr(r, keymbox.msg);
+			r->aux = 0;
+			respond(r, nil);
+			if (r->ifcall.offset + r->ifcall.count >=
+			    strlen(keymbox.msg))
+				keymbox.state = Empty;
+			else
+				keymbox.state = Allocated;
+			break;
+		}
+		keymbox.state = Allocated;
+		for(;;) {
+			if (keymbox.msg == nil)
+				if (recv(keymbox.mchan, nil) < 0) {
+					r->aux = 0;
+					responderror(r);
+					keymbox.state = Empty;
+					threadexits(nil);
+				}
+			if (keymbox.state == Empty)
+				break;
+			else if (keymbox.state == Allocated) {
+				if (keymbox.msg) {
+					readstr(r, keymbox.msg);
+					if (r->ifcall.offset + r->ifcall.count
+					    >= strlen(keymbox.msg)) {
+						free(keymbox.msg);
+						keymbox.msg = nil;
+						keymbox.state = Empty;
+					}
+				}
+				break;
+			}
+		}
+		r->aux = 0;
+		respond(r, nil);
+		break;
+	case Connection:
+		if (c->ctlfd >= 0) {
+			io = ioproc();
+			seek(c->ctlfd, 0, 0);
+			n = ioread(io, c->ctlfd, buf, 10); /* magic 10 */
+			if (n < 0) {
+				r->aux = 0;
+				responderror(r);
+				closeioproc(io);
+				break;
+			}
+			buf[n] = '\0';
+			snprint(path, NETPATHLEN, "%s/tcp/%s/remote", mntpt, buf);
+			if ((fd = ioopen(io, path, OREAD)) < 0 ||
+			    (n = ioread(io, fd, buf, Arbbufsz - 1)) < 0) {
+				r->aux = 0;
+				responderror(r);
+				if (fd >= 0)
+					ioclose(io, fd);
+				closeioproc(io);
+				break;
+			}
+			ioclose(io, fd);
+			closeioproc(io);
+			buf[n] = '\0';
+			readstr(r, buf);
+		} else
+			readstr(r, "::!0\n");
+		r->aux = 0;
+		respond(r, nil);
+		break;
+	case Subchannel:
+		if ((sc->state == Closed || sc->state == Closing ||
+		    sc->state == Eof) && sc->reqq == nil && sc->dataq == nil) {
+			sshdebug(c, "sending EOF1 to channel request listener");
+			r->aux = 0;
+			respond(r, nil);
+			break;
+		}
+		while (sc->reqq == nil) {
+			if (recv(sc->reqchan, nil) < 0) {
+				r->aux = 0;
+				responderror(r);
+				threadexits(nil);
+			}
+			if ((sc->state == Closed || sc->state == Closing ||
+			    sc->state == Eof) && sc->reqq == nil &&
+			    sc->dataq == nil) {
+				sshdebug(c, "sending EOF2 to channel request "
+					"listener");
+				respexit(c, r, nil, nil);
+			}
+		}
+		n = r->ifcall.count;
+		if (sc->reqq->rem < n)
+			n = sc->reqq->rem;
+		if (n > Maxrpcbuf)
+			n = Maxrpcbuf;
+		r->ifcall.offset = 0;
+		readbuf(r, sc->reqq->st, n);
+		sc->reqq->st += n;
+		sc->reqq->rem -= n;
+		if (sc->reqq->rem <= 0) {
+			Plist *d = sc->reqq;
+			sc->reqq = sc->reqq->next;
+			free(d->pack);
+			free(d);
+		}
+		r->aux = 0;
+		respond(r, nil);
+		break;
+	}
+	threadexits(nil);
+}
+
+void
+readdata(void *a)
+{
+	Req *r;
+	Conn *c;
+	SSHChan *sc;
+	int cnum, xconn;
+	uvlong qidpath;
+
+	threadsetname("readdata");
+	r = a;
+	qidpath = (uvlong)r->fid->file->aux;
+	xconn = (qidpath >> Connshift) & Connmask;
+	c = connections[xconn];
+	if (c == nil) {
+		respond(r, "bad connection");
+		sshlog(c, "bad connection");
+		threadexits(nil);
+	}
+	cnum = qidpath & Chanmask;
+	sc = c->chans[cnum];
+	if (sc->dataq == nil && (sc->state == Closed || sc->state == Closing ||
+	    sc->state == Eof)) {
+		sshdebug(c, "sending EOF1 to channel listener");
+		r->aux = 0;
+		respond(r, nil);
+		threadexits(nil);
+	}
+	if (sc->dataq != nil) {
+		getdata(c, sc, r);
+		threadexits(nil);
+	}
+	while (sc->dataq == nil) {
+		if (recv(sc->inchan, nil) < 0) {
+			sshdebug(c, "got interrupt/error in readdata %r");
+			r->aux = 0;
+			responderror(r);
+			threadexits(nil);
+		}
+		if (sc->dataq == nil && (sc->state == Closed ||
+		    sc->state == Closing || sc->state == Eof)) {
+			sshdebug(c, "sending EOF2 to channel listener");
+			r->aux = 0;
+			respond(r, nil);
+			threadexits(nil);
+		}
+	}
+	getdata(c, sc, r);
+	threadexits(nil);
+}
+
+void
+stwrite(Req *r)
+{
+	Conn *c;
+	SSHChan *ch;
+	int lev, xconn;
+	uvlong qidpath;
+
+	threadsetname("stwrite");
+	qidpath = (uvlong)r->fid->file->aux;
+	lev = qidpath >> Levshift;
+	xconn = (qidpath >> Connshift) & Connmask;
+	c = connections[xconn];
+	if (c == nil) {
+		respond(r, "invalid connection");
+		return;
+	}
+	ch = c->chans[qidpath & Chanmask];
+	switch ((ulong)(qidpath & Qtypemask)) {
+	case Qclone:
+	case Qctl:
+		r->aux = (void *)threadcreate(writectlproc, r, Defstk);
+		break;
+	case Qdata:
+		r->ofcall.count = r->ifcall.count;
+		if (lev == Top || lev == Connection ||
+		    c->state == Closed || c->state == Closing ||
+		    ch->state == Closed || ch->state == Closing) {
+			respond(r, nil);
+			break;
+		}
+		if (0 && c->stifle) {		/* was for coexistence */
+			c->stifle = 0;
+			if (deferredinit(c) < 0) {
+				respond(r, "deferredinit failed");
+				break;
+			}
+		}
+		r->aux = (void *)threadcreate(writedataproc, r, Defstk);
+		break;
+	case Qreqrem:
+		r->aux = (void *)threadcreate(writereqremproc, r, Defstk);
+		break;
+	default:
+		respond(r, nil);
+		break;
+	}
+}
+
+static int
+dialbyhand(Conn *c, int ntok, char *toks[])
+{
+	/*
+	 * this uses /net/tcp to connect directly.
+	 * should use dial(2) instead of doing it by hand.
+	 */
+	sshdebug(c, "tcp connect %s %s", toks[1], ntok > 3? toks[2]: "");
+	return fprint(c->ctlfd, "connect %s %s", toks[1], ntok > 3? toks[2]: "");
+}
+
+static void
+userauth(Conn *c, Req *r, char *buf, int ntok, char *toks[])
+{
+	int n;
+	char *attrs[5];
+	Packet *p;
+
+	if (ntok < 3 || ntok > 4)
+		respexit(c, r, buf, "bad connect command");
+	if (!c->service)
+		c->service = estrdup9p(toks[0]);
+	if (c->user)
+		free(c->user);
+	c->user = estrdup9p(toks[2]);
+	sshdebug(c, "userauth for user %s", c->user);
+
+	if (ntok == 4 && strcmp(toks[1], "k") == 0) {
+		if (c->authkey) {
+			free(c->authkey);
+			c->authkey = nil;
+		}
+		if (c->password)
+			free(c->password);
+		c->password = estrdup9p(toks[3]);
+		sshdebug(c, "userauth got password");
+	} else {
+		if (c->password) {
+			free(c->password);
+			c->password = nil;
+		}
+		memset(attrs, 0, sizeof attrs);
+		attrs[0] = "proto=rsa";
+		attrs[1] = "!dk?";
+		attrs[2] = smprint("user=%s", c->user);
+		attrs[3] = smprint("sys=%s", c->remote);
+		if (c->authkey)
+			free(c->authkey);
+		sshdebug(c, "userauth trying rsa");
+		if (ntok == 3)
+			c->authkey = factlookup(4, 2, attrs);
+		else {
+			attrs[4] = toks[3];
+			c->authkey = factlookup(5, 2, attrs);
+		}
+		free(attrs[2]);
+		free(attrs[3]);
+	}
+
+	if (!c->password && !c->authkey)
+		respexit(c, r, buf, "no auth info");
+	else if (c->state != Authing) {
+		p = new_packet(c);
+		add_byte(p, SSH_MSG_SERVICE_REQUEST);
+		add_string(p, c->service);
+		n = finish_packet(p);
+		sshdebug(c, "sending msg svc req for %s", c->service);
+		if (writeio(c->dio, c->datafd, p->nlength, n) != n) {
+			sshdebug(c, "authing write failed: %r");
+			free(p);
+			r->aux = 0;
+			responderror(r);
+			free(buf);
+			threadexits(nil);
+		}
+		free(p);
+	} else
+		if (client_auth(c, c->dio) < 0)
+			respexit(c, r, buf, "ssh-userauth client auth failed");
+	qlock(&c->l);
+	if (c->state != Established) {
+		sshdebug(c, "sleeping for auth");
+		rsleep(&c->r);
+	}
+	qunlock(&c->l);
+	if (c->state != Established)
+		respexit(c, r, buf, "ssh-userath auth failed (not Established)");
+}
+
+void
+writectlproc(void *a)
+{
+	Req *r;
+	Packet *p;
+	Conn *c;
+	SSHChan *ch;
+	char *tcpconn2, *buf, *toks[4];
+	int n, ntok, lev, xconn;
+	uvlong qidpath;
+	char path[NETPATHLEN], tcpconn[Numbsz];
+
+	threadsetname("writectlproc");
+	r = a;
+	qidpath = (uvlong)r->fid->file->aux;
+	lev = qidpath >> Levshift;
+	xconn = (qidpath >> Connshift) & Connmask;
+
+	c = connections[xconn];
+	if (c == nil) {
+		respond(r, "bad connection");
+		sshlog(c, "bad connection");
+		threadexits(nil);
+	}
+	ch = c->chans[qidpath & Chanmask];
+
+	if (r->ifcall.count <= Numbsz)
+		buf = emalloc9p(Numbsz + 1);
+	else
+		buf = emalloc9p(r->ifcall.count + 1);
+	memmove(buf, r->ifcall.data, r->ifcall.count);
+	buf[r->ifcall.count] = '\0';
+
+	sshdebug(c, "level %d writectl: %s", lev, buf);
+	ntok = tokenize(buf, toks, nelem(toks));
+	switch (lev) {
+	case Connection:
+		if (strcmp(toks[0], "id") == 0) {	/* was for sshswitch */
+			if (ntok < 2)
+				respexit(c, r, buf, "bad id request");
+			strncpy(c->idstring, toks[1], sizeof c->idstring);
+			sshdebug(c, "id %s", toks[1]);
+			break;
+		}
+		if (strcmp(toks[0], "connect") == 0) {
+			if (ntok < 2)
+				respexit(c, r, buf, "bad connect request");
+			/*
+			 * should use dial(2) instead of doing it by hand.
+			 */
+			memset(tcpconn, '\0', sizeof(tcpconn));
+			pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
+			dialbyhand(c, ntok, toks);
+
+			c->role = Client;
+			/* Override the PKA list; we can take any in */
+			pkas[0] = &rsa_pka;
+			pkas[1] = &dss_pka;
+			pkas[2] = nil;
+			tcpconn2 = estrdup9p(tcpconn);
+
+			/* swap id strings, negotiate crypto */
+			if (dohandshake(c, tcpconn2) < 0) {
+				sshlog(c, "connect handshake failed: "
+					"tcp conn %s", tcpconn2);
+				free(tcpconn2);
+				respexit(c, r, buf, "connect handshake failed");
+			}
+			free(tcpconn2);
+			keymbox.state = Empty;
+			nbsendul(keymbox.mchan, 1);
+			break;
+		}
+
+		if (c->state == Closed || c->state == Closing)
+			respexit(c, r, buf, "connection closed");
+		if (strcmp(toks[0], "ssh-userauth") == 0)
+			userauth(c, r, buf, ntok, toks);
+		else if (strcmp(toks[0], "ssh-connection") == 0) {
+			/* your ad here */
+		} else if (strcmp(toks[0], "hangup") == 0) {
+			if (c->rpid >= 0)
+				threadint(c->rpid);
+			shutdown(c);
+		} else if (strcmp(toks[0], "announce") == 0) {
+			sshdebug(c, "got %s argument for announce", toks[1]);
+			write(c->ctlfd, r->ifcall.data, r->ifcall.count);
+		} else if (strcmp(toks[0], "accept") == 0) {
+			/* should use dial(2) instead of diddling /net/tcp */
+			memset(tcpconn, '\0', sizeof(tcpconn));
+			pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
+			fprint(c->ctlfd, "accept %s", tcpconn);
+
+			c->role = Server;
+			tcpconn2 = estrdup9p(tcpconn);
+			/* swap id strings, negotiate crypto */
+			if (dohandshake(c, tcpconn2) < 0) {
+				sshlog(c, "accept handshake failed: "
+					"tcp conn %s", tcpconn2);
+				free(tcpconn2);
+				shutdown(c);
+				respexit(c, r, buf, "accept handshake failed");
+			}
+			free(tcpconn2);
+		} else if (strcmp(toks[0], "reject") == 0) {
+			memset(tcpconn, '\0', sizeof(tcpconn));
+			pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
+
+			snprint(path, NETPATHLEN, "%s/tcp/%s/data", mntpt, tcpconn);
+			c->datafd = open(path, ORDWR);
+
+			p = new_packet(c);
+			add_byte(p, SSH_MSG_DISCONNECT);
+			add_byte(p, SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT);
+			add_string(p, toks[2]);
+			add_string(p, "EN");
+			n = finish_packet(p);
+			if (c->dio && c->datafd >= 0)
+				iowrite(c->dio, c->datafd, p->nlength, n);
+			free(p);
+			if (c->ctlfd >= 0)
+				fprint(c->ctlfd, "reject %s %s", buf, toks[2]);
+			if (c->rpid >= 0)
+				threadint(c->rpid);
+			shutdown(c);
+		}
+		break;
+	case Subchannel:
+		if (c->state == Closed || c->state == Closing)
+			respexit(c, r, buf, "channel closed");
+		if (strcmp(toks[0], "connect") == 0) {
+			p = new_packet(c);
+			add_byte(p, SSH_MSG_CHANNEL_OPEN);
+			sshdebug(c, "chan writectl: connect %s",
+				ntok > 1? toks[1]: "session");
+			add_string(p, ntok > 1? toks[1]: "session");
+			add_uint32(p, ch->id);
+			add_uint32(p, Maxpayload);
+			add_uint32(p, Maxrpcbuf);
+			/* more stuff if it's an x11 session */
+			n = finish_packet(p);
+			iowrite(c->dio, c->datafd, p->nlength, n);
+			free(p);
+			qlock(&c->l);
+			if (ch->otherid == -1)
+				rsleep(&ch->r);
+			qunlock(&c->l);
+		} else if (strcmp(toks[0], "global") == 0) {
+			/* your ad here */
+		} else if (strcmp(toks[0], "hangup") == 0) {
+			if (ch->state != Closed && ch->state != Closing) {
+				ch->state = Closing;
+				if (ch->otherid != -1) {
+					p = new_packet(c);
+					add_byte(p, SSH_MSG_CHANNEL_CLOSE);
+					add_uint32(p, ch->otherid);
+					n = finish_packet(p);
+					iowrite(c->dio, c->datafd, p->nlength, n);
+					free(p);
+				}
+				qlock(&c->l);
+				rwakeup(&ch->r);
+				qunlock(&c->l);
+				nbsendul(ch->inchan, 1);
+				nbsendul(ch->reqchan, 1);
+			}
+			for (n = 0; n < MAXCONN && (c->chans[n] == nil ||
+			    c->chans[n]->state == Empty ||
+			    c->chans[n]->state == Closing ||
+			    c->chans[n]->state == Closed); ++n)
+				;
+			if (n >= MAXCONN) {
+				if (c->rpid >= 0)
+					threadint(c->rpid);
+				shutdown(c);
+			}
+		} else if (strcmp(toks[0], "announce") == 0) {
+			sshdebug(c, "got argument `%s' for chan announce",
+				toks[1]);
+			free(ch->ann);
+			ch->ann = estrdup9p(toks[1]);
+		}
+		break;
+	}
+	r->ofcall.count = r->ifcall.count;
+	r->aux = 0;
+	respond(r, nil);
+	free(buf);
+	threadexits(nil);
+}
+
+void
+writereqremproc(void *a)
+{
+	Req *r;
+	Packet *p;
+	Conn *c;
+	SSHChan *ch;
+	char *cmd, *q, *buf, *toks[4];
+	int n, ntok, lev, xconn;
+	uvlong qidpath;
+
+	threadsetname("writereqremproc");
+	r = a;
+	qidpath = (uvlong)r->fid->file->aux;
+	lev = qidpath >> Levshift;
+	xconn = (qidpath >> Connshift) & Connmask;
+	c = connections[xconn];
+	if (c == nil) {
+		respond(r, "Invalid connection");
+		threadexits(nil);
+	}
+	ch = c->chans[qidpath & Chanmask];
+	if (r->ifcall.count <= 10)
+		buf = emalloc9p(10 + 1);
+	else
+		buf = emalloc9p(r->ifcall.count + 1);
+	memmove(buf, r->ifcall.data, r->ifcall.count);
+	buf[r->ifcall.count] = '\0';
+	sshdebug(c, "writereqrem: %s", buf);
+	ntok = tokenize(buf, toks, nelem(toks));
+
+	if (lev == Top) {
+		free(keymbox.msg);
+		keymbox.msg = buf;
+		nbsendul(keymbox.mchan, 1);
+		r->ofcall.count = r->ifcall.count;
+		respexit(c, r, nil, nil);
+	}
+
+	r->ofcall.count = r->ifcall.count;
+	if (c->state == Closed  || c->state == Closing ||
+	    ch->state == Closed || ch->state == Closing)
+		respexit(c, r, buf, nil);
+
+	p = new_packet(c);
+	if (strcmp(toks[0], "success") == 0) {
+		add_byte(p, SSH_MSG_CHANNEL_SUCCESS);
+		add_uint32(p, ch->otherid);
+	} else if (strcmp(toks[0], "failure") == 0) {
+		add_byte(p, SSH_MSG_CHANNEL_FAILURE);
+		add_uint32(p, ch->otherid);
+	} else if (strcmp(toks[0], "close") == 0) {
+		ch->state = Closing;
+		add_byte(p, SSH_MSG_CHANNEL_CLOSE);
+		add_uint32(p, ch->otherid);
+	} else if (strcmp(toks[0], "shell") == 0) {
+		ch->state = Established;
+		/*
+		 * Some servers *cough*OpenSSH*cough* don't seem to be able
+		 * to intelligently handle a shell with no pty.
+		 */
+		add_byte(p, SSH_MSG_CHANNEL_REQUEST);
+		add_uint32(p, ch->otherid);
+		add_string(p, "pty-req");
+		add_byte(p, 0);
+		if (ntok == 1)
+			add_string(p, "dumb");
+		else
+			add_string(p, toks[1]);
+		add_uint32(p, 0);
+		add_uint32(p, 0);
+		add_uint32(p, 0);
+		add_uint32(p, 0);
+		add_string(p, "");
+		n = finish_packet(p);
+		iowrite(c->dio, c->datafd, p->nlength, n);
+		init_packet(p);
+		p->c = c;
+		add_byte(p, SSH_MSG_CHANNEL_REQUEST);
+		add_uint32(p, ch->otherid);
+		add_string(p, "shell");
+		add_byte(p, 0);
+		sshdebug(c, "sending shell request: rlength=%lud twindow=%lud",
+			p->rlength, ch->twindow);
+	} else if (strcmp(toks[0], "exec") == 0) {
+		ch->state = Established;
+		add_byte(p, SSH_MSG_CHANNEL_REQUEST);
+		add_uint32(p, ch->otherid);
+		add_string(p, "exec");
+		add_byte(p, 0);
+		cmd = emalloc9p(Bigbufsz);
+		q = seprint(cmd, cmd+Bigbufsz, "%s", toks[1]);
+		for (n = 2; n < ntok; ++n) {
+			q = seprint(q, cmd+Bigbufsz, " %s", toks[n]);
+			if (q == nil)
+				break;
+		}
+		add_string(p, cmd);
+		free(cmd);
+	} else
+		respexit(c, r, buf, "bad request command");
+	n = finish_packet(p);
+	iowrite(c->dio, c->datafd, p->nlength, n);
+	free(p);
+	respexit(c, r, buf, nil);
+}
+
+void
+writedataproc(void *a)
+{
+	Req *r;
+	Packet *p;
+	Conn *c;
+	SSHChan *ch;
+	int n, xconn;
+	uvlong qidpath;
+
+	threadsetname("writedataproc");
+	r = a;
+	qidpath = (uvlong)r->fid->file->aux;
+	xconn = (qidpath >> Connshift) & Connmask;
+	c = connections[xconn];
+	if (c == nil) {
+		respond(r, "Invalid connection");
+		threadexits(nil);
+	}
+	ch = c->chans[qidpath & Chanmask];
+
+	p = new_packet(c);
+	add_byte(p, SSH_MSG_CHANNEL_DATA);
+	hnputl(p->payload+1, ch->otherid);
+	p->rlength += 4;
+	add_block(p, r->ifcall.data, r->ifcall.count);
+	n = finish_packet(p);
+
+	if (ch->sent + p->rlength > ch->twindow) {
+		qlock(&ch->xmtlock);
+		while (ch->sent + p->rlength > ch->twindow)
+			rsleep(&ch->xmtrendez);
+		qunlock(&ch->xmtlock);
+	}
+	iowrite(c->dio, c->datafd, p->nlength, n);
+	respexit(c, r, p, nil);
+}
+
+/*
+ * Although this is named stclunk, it's attached to the destroyfid
+ * member of the Srv struct.  It turns out there's no member
+ * called clunk.  But if there are no other references, a 9P Tclunk
+ * will end up calling destroyfid.
+ */
+void
+stclunk(Fid *f)
+{
+	Packet *p;
+	Conn *c;
+	SSHChan *sc;
+	int n, lev, cnum, chnum;
+	uvlong qidpath;
+
+	threadsetname("stclunk");
+	if (f == nil || f->file == nil)
+		return;
+	qidpath = (uvlong)f->file->aux;
+	lev = qidpath >> Levshift;
+	cnum = (qidpath >> Connshift) & Connmask;
+	chnum = qidpath & Chanmask;
+	c = connections[cnum];
+	sshdebug(c, "got clunk on file: %#llux %d %d %d: %s",
+		qidpath, lev, cnum, chnum, f->file->name);
+	/* qidpath test implies conn 0, chan 0 */
+	if (lev == Top && qidpath == Qreqrem) {
+		if (keymbox.state != Empty) {
+			keymbox.state = Empty;
+			// nbsendul(keymbox.mchan, 1);
+		}
+		keymbox.msg = nil;
+		return;
+	}
+
+	if (c == nil)
+		return;
+	if (lev == Connection && (qidpath & Qtypemask) == Qctl &&
+	    (c->state == Opening || c->state == Negotiating ||
+	     c->state == Authing)) {
+		for (n = 0; n < MAXCONN && (!c->chans[n] ||
+		    c->chans[n]->state == Empty ||
+		    c->chans[n]->state == Closed ||
+		    c->chans[n]->state == Closing); ++n)
+			;
+		if (n >= MAXCONN) {
+			if (c->rpid >= 0)
+				threadint(c->rpid);
+			shutdown(c);
+		}
+		return;
+	}
+
+	sc = c->chans[chnum];
+	if (lev != Subchannel)
+		return;
+	if ((qidpath & Qtypemask) == Qlisten && sc->state == Listening) {
+		qlock(&c->l);
+		if (sc->state != Closed) {
+			sc->state = Closed;
+			chanclose(sc->inchan);
+			chanclose(sc->reqchan);
+		}
+		qunlock(&c->l);
+	} else if ((qidpath & Qtypemask) == Qdata && sc->state != Empty &&
+	    sc->state != Closed && sc->state != Closing) {
+		if (f->file != sc->data && f->file != sc->request) {
+			sshlog(c, "great evil is upon us; destroying a fid "
+				"we didn't create");
+			return;
+		}
+
+		p = new_packet(c);
+		add_byte(p, SSH_MSG_CHANNEL_CLOSE);
+		hnputl(p->payload+1, sc->otherid);
+		p->rlength += 4;
+		n = finish_packet(p);
+		sc->state = Closing;
+		iowrite(c->dio, c->datafd, p->nlength, n);
+		free(p);
+
+		qlock(&c->l);
+		rwakeup(&sc->r);
+		qunlock(&c->l);
+		nbsendul(sc->inchan, 1);
+		nbsendul(sc->reqchan, 1);
+	}
+	for (n = 0; n < MAXCONN && (!c->chans[n] ||
+	    c->chans[n]->state == Empty || c->chans[n]->state == Closed ||
+	    c->chans[n]->state == Closing); ++n)
+		;
+	if (n >= MAXCONN) {
+		if (c->rpid >= 0)
+			threadint(c->rpid);
+		shutdown(c);
+	}
+}
+
+void
+stflush(Req *r)
+{
+	Req *or;
+	uvlong qidpath;
+
+	threadsetname("stflush");
+	or = r->oldreq;
+	qidpath = (uvlong)or->fid->file->aux;
+	sshdebug(nil, "got flush on file %#llux %lld %lld %lld: %s %#p",
+		argv0, qidpath, qidpath >> Levshift,
+		(qidpath >> Connshift) & Connmask, qidpath & Chanmask,
+		or->fid->file->name, or->aux);
+	if (!or->aux)
+		respond(or, "interrupted");
+	else if (or->ifcall.type == Topen && (qidpath & Qtypemask) == Qlisten &&
+	    (qidpath >> Levshift) == Connection ||
+	    or->ifcall.type == Tread && (qidpath & Qtypemask) == Qdata &&
+	    (qidpath >> Levshift) == Subchannel ||
+	    or->ifcall.type == Tread && (qidpath & Qtypemask) == Qreqrem)
+		threadint((uintptr)or->aux);
+	else {
+		threadkill((uintptr)or->aux);
+		or->aux = 0;
+		respond(or, "interrupted");
+	}
+	respond(r, nil);
+}
+
+void
+filedup(Req *r, File *src)
+{
+	r->ofcall.qid = src->qid;
+	closefile(r->fid->file);
+	r->fid->file = src;
+	incref(src);
+}
+
+Conn *
+alloc_conn(void)
+{
+	int slevconn, i, s, firstnil;
+	char buf[Numbsz];
+	Conn *c;
+	static QLock aclock;
+
+	qlock(&aclock);
+	firstnil = -1;
+	for (i = 0; i < MAXCONN; ++i) {
+		if (connections[i] == nil) {
+			if (firstnil == -1)
+				firstnil = i;
+			continue;
+		}
+		s = connections[i]->state;
+		if (s == Empty || s == Closed)
+			break;
+	}
+	if (i >= MAXCONN) {
+		if (firstnil == -1) {		/* all slots in use? */
+			qunlock(&aclock);
+			return nil;
+		}
+		/* no reusable slots, allocate a new Conn */
+		connections[firstnil] = emalloc9p(sizeof(Conn));
+		memset(connections[firstnil], 0, sizeof(Conn));
+		i = firstnil;
+	}
+
+	c = connections[i];
+	memset(&c->r, '\0', sizeof(Rendez));
+	c->r.l = &c->l;
+	c->dio = ioproc();
+	c->rio = nil;
+	c->state = Allocated;
+	c->role = Server;
+	c->id = i;
+	c->stifle = c->poisoned = 0;
+	c->user = c->service = nil;
+	c->inseq = c->nchan = c->outseq = 0;
+	c->cscrypt = c->csmac = c->ctlfd = c->datafd = c->decrypt =
+		c->encrypt = c->inmac = c->ncscrypt = c->ncsmac =
+		c->nsccrypt = c->nscmac = c->outmac = c->rpid = c->sccrypt =
+		c->scmac = c->tcpconn = -1;
+	if (c->e) {
+		mpfree(c->e);
+		c->e = nil;
+	}
+	if (c->x) {
+		mpfree(c->x);
+		c->x = nil;
+	}
+
+	snprint(buf, sizeof buf, "%d", i);
+	if (c->dir == nil) {
+		slevconn = Connection << Levshift | i << Connshift;
+		c->dir = createfile(rootfile, buf, uid, 0555|DMDIR,
+			(void *)(slevconn | Qroot));
+		c->clonefile = createfile(c->dir, "clone", uid, 0666,
+			(void *)(slevconn | Qclone));
+		c->ctlfile = createfile(c->dir, "ctl", uid, 0666,
+			(void *)(slevconn | Qctl));
+		c->datafile = createfile(c->dir, "data", uid, 0666,
+			(void *)(slevconn | Qdata));
+		c->listenfile = createfile(c->dir, "listen", uid, 0666,
+			(void *)(slevconn | Qlisten));
+		c->localfile = createfile(c->dir, "local", uid, 0444,
+			(void *)(slevconn | Qlocal));
+		c->remotefile = createfile(c->dir, "remote", uid, 0444,
+			(void *)(slevconn | Qreqrem));
+		c->statusfile = createfile(c->dir, "status", uid, 0444,
+			(void *)(slevconn | Qstatus));
+		c->tcpfile = createfile(c->dir, "tcp", uid, 0444,
+			(void *)(slevconn | Qtcp));
+	}
+//	c->skexinit = c->rkexinit = nil;
+	c->got_sessid = 0;
+	c->otherid = nil;
+	c->inik = c->outik = nil;
+	c->s2ccs = c->c2scs = c->enccs = c->deccs = nil;
+	qunlock(&aclock);
+	return c;
+}
+
+SSHChan *
+alloc_chan(Conn *c)
+{
+	int cnum, slcn;
+	char buf[Numbsz];
+	Plist *p, *next;
+	SSHChan *sc;
+
+	if (c->nchan >= MAXCONN)
+		return nil;
+	qlock(&c->l);
+	cnum = c->nchan;
+	if (c->chans[cnum] == nil) {
+		c->chans[cnum] = emalloc9p(sizeof(SSHChan));
+		memset(c->chans[cnum], 0, sizeof(SSHChan));
+	}
+	sc = c->chans[cnum];
+	snprint(buf, sizeof buf, "%d", cnum);
+	memset(&sc->r, '\0', sizeof(Rendez));
+	sc->r.l = &c->l;
+	sc->id = cnum;
+	sc->state = Empty;
+	sc->conn = c->id;
+	sc->otherid = sc->waker = -1;
+	sc->sent = sc->twindow = sc->rwindow = sc->inrqueue = 0;
+	sc->ann = nil;
+	sc->lreq = nil;
+
+	if (sc->dir == nil) {
+		slcn = Subchannel << Levshift | c->id << Connshift | cnum;
+		sc->dir = createfile(c->dir, buf, uid, 0555|DMDIR,
+			(void *)(slcn | Qroot));
+		sc->ctl = createfile(sc->dir, "ctl", uid, 0666,
+			(void *)(slcn | Qctl));
+		sc->data = createfile(sc->dir, "data", uid, 0666,
+			(void *)(slcn | Qdata));
+		sc->listen = createfile(sc->dir, "listen", uid, 0666,
+			(void *)(slcn | Qlisten));
+		sc->request = createfile(sc->dir, "request", uid, 0666,
+			(void *)(slcn | Qreqrem));
+		sc->status = createfile(sc->dir, "status", uid, 0444,
+			(void *)(slcn | Qstatus));
+		sc->tcp = createfile(sc->dir, "tcp", uid, 0444,
+			(void *)(slcn | Qtcp));
+	}
+	c->nchan++;
+
+	for (; sc->reqq != nil; sc->reqq = next) {
+		p = sc->reqq;
+		next = p->next;
+		free(p->pack);
+		free(p);
+	}
+	sc->dataq = sc->datatl = sc->reqtl = nil;
+
+	if (sc->inchan)
+		chanfree(sc->inchan);
+	sc->inchan = chancreate(4, 0);
+
+	if (sc->reqchan)
+		chanfree(sc->reqchan);
+	sc->reqchan = chancreate(4, 0);
+
+	memset(&sc->xmtrendez, '\0', sizeof(Rendez));
+	sc->xmtrendez.l = &sc->xmtlock;
+	qunlock(&c->l);
+	return sc;
+}
+
+static int
+readlineio(Conn *, Ioproc *io, int fd, char *buf, int size)
+{
+	int n;
+	char *p;
+
+	for (p = buf; p < buf + size - 1; p++) {
+		n = ioread(io, fd, p, 1);
+		if (n != 1 || *p == '\n') {
+			*p = '\0';
+			break;
+		}
+	}
+	return p - buf;
+}
+
+static char *
+readremote(Conn *c, Ioproc *io, char *tcpconn)
+{
+	int n, remfd;
+	char *p, *remote;
+	char path[Arbbufsz], buf[NETPATHLEN];
+
+	remote = nil;
+	snprint(path, sizeof path, "%s/tcp/%s/remote", mntpt, tcpconn);
+	remfd = ioopen(io, path, OREAD);
+	if (remfd < 0) {
+		sshlog(c, "readremote: can't open %s: %r", path);
+		return nil;
+	}
+	n = ioread(io, remfd, buf, sizeof buf - 1);
+	if (n > 0) {
+		buf[n] = 0;
+		p = strchr(buf, '!');
+		if (p)
+			*p = 0;
+		remote = estrdup9p(buf);
+	}
+	ioclose(io, remfd);
+	return remote;
+}
+
+static void
+sendmyid(Conn *c, Ioproc *io)
+{
+	char path[Arbbufsz];
+
+	snprint(path, sizeof path, "%s\r\n", MYID);
+	iowrite(io, c->datafd, path, strlen(path));
+}
+
+/* save and tidy up the remote id */
+static void
+stashremid(Conn *c, char *remid)
+{
+	char *nl;
+
+	if (c->otherid)
+		free(c->otherid);
+	c->otherid = estrdup9p(remid);
+
+	nl = strchr(c->otherid, '\n');
+	if (nl)
+		*nl = '\0';
+	nl = strchr(c->otherid, '\r');
+	if (nl)
+		*nl = '\0';
+}
+
+static void
+hangupconn(Conn *c)
+{
+	hangup(c->ctlfd);
+	close(c->ctlfd);
+	close(c->datafd);
+	c->ctlfd = c->datafd = -1;
+}
+
+#ifdef COEXIST
+static int
+exchids(Conn *c, Ioproc *io, char *remid, int remsz)
+{
+	int n;
+
+	/*
+	 * exchange versions.  server writes id, then reads;
+	 * client reads id then writes (in theory).
+	 */
+	if (c->role == Server) {
+		sendmyid(c, io);
+
+		n = readlineio(c, io, c->datafd, remid, remsz);
+		if (n < 5)		/* can't be a valid SSH id string */
+			return -1;
+		sshdebug(c, "dohandshake: server, got `%s', sent `%s'", remid,
+			MYID);
+	} else {
+		/* client: read server's id */
+		n = readlineio(c, io, c->datafd, remid, remsz);
+		if (n < 5)		/* can't be a valid SSH id string */
+			return -1;
+
+		sendmyid(c, io);
+		sshdebug(c, "dohandshake: client, got `%s' sent `%s'", remid, MYID);
+		if (remid[0] == '\0') {
+			sshlog(c, "dohandshake: client, empty remote id string;"
+				" out of sync");
+			return -1;
+		}
+	}
+	sshdebug(c, "remote id string `%s'", remid);
+	return 0;
+}
+
+/*
+ * negotiate the protocols.
+ * We don't do the full negotiation here, because we also have
+ * to handle a re-negotiation request from the other end.
+ * So we just kick it off and let the receiver process take it from there.
+ */
+static int
+negotiate(Conn *c)
+{
+	send_kexinit(c);
+
+	qlock(&c->l);
+	if ((c->role == Client && c->state != Negotiating) ||
+	    (c->role == Server && c->state != Established)) {
+		sshdebug(c, "awaiting establishment");
+		rsleep(&c->r);
+	}
+	qunlock(&c->l);
+
+	if (c->role == Server && c->state != Established ||
+	    c->role == Client && c->state != Negotiating) {
+		sshdebug(c, "failed to establish");
+		return -1;
+	}
+	sshdebug(c, "established; crypto now on");
+	return 0;
+}
+
+/* this was deferred when trying to make coexistence with v1 work */
+static int
+deferredinit(Conn *c)
+{
+	char remid[Arbbufsz];
+	Ioproc *io;
+
+	io = ioproc();
+	/*
+	 * don't bother checking the remote's id string.
+	 * as a client, we can cope with v1 if we don't verify the host key.
+	 */
+	if (exchids(c, io, remid, sizeof remid) < 0 ||
+	    0 && c->role == Client && strncmp(remid, "SSH-2", 5) != 0 &&
+	    strncmp(remid, "SSH-1.99", 8) != 0) {
+		/* not a protocol version we know; give up */
+		closeioproc(io);
+		hangupconn(c);
+		return -1;
+	}
+	closeioproc(io);
+	stashremid(c, remid);
+
+	c->state = Initting;
+
+	/* start the reader thread */
+	if (c->rpid < 0)
+		c->rpid = threadcreate(reader, c, Defstk);
+
+	return negotiate(c);
+}
+
+int
+dohandshake(Conn *c, char *tcpconn)
+{
+	int tcpdfd;
+	char *remote;
+	char path[Arbbufsz];
+	Ioproc *io;
+
+	io = ioproc();
+
+	/* read tcp conn's remote address into c->remote */
+	remote = readremote(c, io, tcpconn);
+	if (remote) {
+		free(c->remote);
+		c->remote = remote;
+	}
+
+	/* open tcp conn's data file */
+	c->tcpconn = atoi(tcpconn);
+	snprint(path, sizeof path, "%s/tcp/%s/data", mntpt, tcpconn);
+	tcpdfd = ioopen(io, path, ORDWR);
+	closeioproc(io);
+	if (tcpdfd < 0) {
+		sshlog(c, "dohandshake: can't open %s: %r", path);
+		return -1;
+	}
+	c->datafd = tcpdfd;		/* underlying tcp data descriptor */
+
+	return deferredinit(c);
+}
+#endif					/* COEXIST */
+
+int
+dohandshake(Conn *c, char *tcpconn)
+{
+	int fd, n;
+	char *p, *othid;
+	char path[Arbbufsz], buf[NETPATHLEN];
+	Ioproc *io;
+
+	io = ioproc();
+	snprint(path, sizeof path, "%s/tcp/%s/remote", mntpt, tcpconn);
+	fd = ioopen(io, path, OREAD);
+	n = ioread(io, fd, buf, sizeof buf - 1);
+	if (n > 0) {
+		buf[n] = 0;
+		p = strchr(buf, '!');
+		if (p)
+			*p = 0;
+		free(c->remote);
+		c->remote = estrdup9p(buf);
+	}
+	ioclose(io, fd);
+
+	snprint(path, sizeof path, "%s/tcp/%s/data", mntpt, tcpconn);
+	fd = ioopen(io, path, ORDWR);
+	if (fd < 0) {
+		closeioproc(io);
+		return -1;
+	}
+	c->datafd = fd;
+
+	/* exchange versions--we're only doing SSH2, unfortunately */
+
+	snprint(path, sizeof path, "%s\r\n", MYID);
+	if (c->idstring && c->idstring[0])
+		strncpy(path, c->idstring, sizeof path);
+	else {
+		iowrite(io, fd, path, strlen(path));
+		p = path;
+		n = 0;
+		do {
+			if (ioread(io, fd, p, 1) < 0) {
+				fprint(2, "%s: short read in ID exchange: %r\n",
+					argv0);
+				break;
+			}
+			++n;
+		} while (*p++ != '\n');
+		if (n < 5) {		/* can't be a valid SSH id string */
+			close(fd);
+			goto err;
+		}
+		*p = 0;
+	}
+	sshdebug(c, "id string `%s'", path);
+	if (c->idstring[0] == '\0' &&
+	    strncmp(path, "SSH-2", 5) != 0 &&
+	    strncmp(path, "SSH-1.99", 8) != 0) {
+		/* not a protocol version we know; give up */
+		ioclose(io, fd);
+		goto err;
+	}
+	closeioproc(io);
+
+	if (c->otherid)
+		free(c->otherid);
+	c->otherid = othid = estrdup9p(path);
+	for (n = strlen(othid) - 1; othid[n] == '\r' || othid[n] == '\n'; --n)
+		othid[n] = '\0';
+	c->state = Initting;
+
+	/* start the reader thread */
+	if (c->rpid < 0)
+		c->rpid = threadcreate(reader, c, Defstk);
+
+	/*
+	 * negotiate the protocols
+	 * We don't do the full negotiation here, because we also have
+	 * to handle a re-negotiation request from the other end.  So
+	 * we just kick it off and let the receiver process take it from there.
+	 */
+
+	send_kexinit(c);
+
+	qlock(&c->l);
+	if ((c->role == Client && c->state != Negotiating) ||
+	    (c->role == Server && c->state != Established))
+		rsleep(&c->r);
+	qunlock(&c->l);
+	if (c->role == Server && c->state != Established ||
+	    c->role == Client && c->state != Negotiating)
+		return -1;
+	return 0;
+err:
+	/* should use hangup in dial(2) instead of diddling /net/tcp */
+	snprint(path, sizeof path, "%s/tcp/%s/ctl", mntpt, tcpconn);
+	fd = ioopen(io, path, OWRITE);
+	iowrite(io, fd, "hangup", 6);
+	ioclose(io, fd);
+	closeioproc(io);
+	return -1;
+}
+
+void
+send_kexinit(Conn *c)
+{
+	Packet *ptmp;
+	char *buf, *p, *e;
+	int i, msglen;
+
+	sshdebug(c, "initializing kexinit packet");
+	if (c->skexinit != nil)
+		free(c->skexinit);
+	c->skexinit = new_packet(c);
+
+	buf = emalloc9p(Bigbufsz);
+	buf[0] = (uchar)SSH_MSG_KEXINIT;
+
+	add_packet(c->skexinit, buf, 1);
+	for (i = 0; i < 16; ++i)
+		buf[i] = fastrand();
+
+	add_packet(c->skexinit, buf, 16);		/* cookie */
+	e = buf + Bigbufsz - 1;
+	p = seprint(buf, e, "%s", kexes[0]->name);
+	for (i = 1; i < nelem(kexes); ++i)
+		p = seprint(p, e, ",%s", kexes[i]->name);
+	sshdebug(c, "sent KEX algs: %s", buf);
+
+	add_string(c->skexinit, buf);		/* Key exchange */
+	if (pkas[0] == nil)
+		add_string(c->skexinit, "");
+	else{
+		p = seprint(buf, e, "%s", pkas[0]->name);
+		for (i = 1; i < nelem(pkas) && pkas[i] != nil; ++i)
+			p = seprint(p, e, ",%s", pkas[i]->name);
+		sshdebug(c, "sent host key algs: %s", buf);
+		add_string(c->skexinit, buf);		/* server's key algs */
+	}
+
+	p = seprint(buf, e, "%s", cryptos[0]->name);
+	for (i = 1; i < nelem(cryptos); ++i)
+		p = seprint(p, e, ",%s", cryptos[i]->name);
+	sshdebug(c, "sent crypto algs: %s", buf);
+
+	add_string(c->skexinit, buf);		/* c->s crypto */
+	add_string(c->skexinit, buf);		/* s->c crypto */
+	p = seprint(buf, e, "%s", macnames[0]);
+	for (i = 1; i < nelem(macnames); ++i)
+		p = seprint(p, e, ",%s", macnames[i]);
+	sshdebug(c, "sent MAC algs: %s", buf);
+
+	add_string(c->skexinit, buf);		/* c->s mac */
+	add_string(c->skexinit, buf);		/* s->c mac */
+	add_string(c->skexinit, "none");	/* c->s compression */
+	add_string(c->skexinit, "none");	/* s->c compression */
+	add_string(c->skexinit, "");		/* c->s languages */
+	add_string(c->skexinit, "");		/* s->c languages */
+	memset(buf, 0, 5);
+	add_packet(c->skexinit, buf, 5);
+
+	ptmp = new_packet(c);
+	memmove(ptmp, c->skexinit, sizeof(Packet));
+	msglen = finish_packet(ptmp);
+
+	if (c->dio && c->datafd >= 0)
+		iowrite(c->dio, c->datafd, ptmp->nlength, msglen);
+	free(ptmp);
+	free(buf);
+}
+
+static void
+establish(Conn *c)
+{
+	qlock(&c->l);
+	c->state = Established;
+	rwakeup(&c->r);
+	qunlock(&c->l);
+}
+
+static int
+negotiating(Conn *c, Packet *p, Packet *p2, char *buf, int size)
+{
+	int i, n;
+
+	USED(size);
+	switch (p->payload[0]) {
+	case SSH_MSG_DISCONNECT:
+		if (debug) {
+			get_string(p, p->payload + 5, buf, Arbbufsz, nil);
+			sshdebug(c, "got disconnect: %s", buf);
+		}
+		return -1;
+	case SSH_MSG_NEWKEYS:
+		/*
+		 * If we're just updating, go straight to
+		 * established, otherwise wait for auth'n.
+		 */
+		i = c->encrypt;
+		memmove(c->c2siv, c->nc2siv, SHA1dlen*2);
+		memmove(c->s2civ, c->ns2civ, SHA1dlen*2);
+		memmove(c->c2sek, c->nc2sek, SHA1dlen*2);
+		memmove(c->s2cek, c->ns2cek, SHA1dlen*2);
+		memmove(c->c2sik, c->nc2sik, SHA1dlen*2);
+		memmove(c->s2cik, c->ns2cik, SHA1dlen*2);
+		c->cscrypt = c->ncscrypt;
+		c->sccrypt = c->nsccrypt;
+		c->csmac = c->ncsmac;
+		c->scmac = c->nscmac;
+		c->c2scs = cryptos[c->cscrypt]->init(c, 0);
+		c->s2ccs = cryptos[c->sccrypt]->init(c, 1);
+		if (c->role == Server) {
+			c->encrypt = c->sccrypt;
+			c->decrypt = c->cscrypt;
+			c->outmac = c->scmac;
+			c->inmac = c->csmac;
+			c->enccs = c->s2ccs;
+			c->deccs = c->c2scs;
+			c->outik = c->s2cik;
+			c->inik = c->c2sik;
+		} else{
+			c->encrypt = c->cscrypt;
+			c->decrypt = c->sccrypt;
+			c->outmac = c->csmac;
+			c->inmac = c->scmac;
+			c->enccs = c->c2scs;
+			c->deccs = c->s2ccs;
+			c->outik = c->c2sik;
+			c->inik = c->s2cik;
+		}
+		sshdebug(c, "using %s for encryption and %s for decryption",
+			cryptos[c->encrypt]->name, cryptos[c->decrypt]->name);
+		qlock(&c->l);
+		if (i != -1)
+			c->state = Established;
+		if (c->role == Client)
+			rwakeup(&c->r);
+		qunlock(&c->l);
+		break;
+	case SSH_MSG_KEXDH_INIT:
+		kexes[c->kexalg]->serverkex(c, p);
+		break;
+	case SSH_MSG_KEXDH_REPLY:
+		init_packet(p2);
+		p2->c = c;
+		if (kexes[c->kexalg]->clientkex2(c, p) < 0) {
+			add_byte(p2, SSH_MSG_DISCONNECT);
+			add_byte(p2, SSH_DISCONNECT_KEY_EXCHANGE_FAILED);
+			add_string(p2, "Key exchange failure");
+			add_string(p2, "");
+			n = finish_packet(p2);
+			iowrite(c->rio, c->datafd, p2->nlength, n);
+			shutdown(c);
+			free(p);
+			free(p2);
+			closeioproc(c->rio);
+			c->rio = nil;
+			c->rpid = -1;
+
+			qlock(&c->l);
+			rwakeup(&c->r);
+			qunlock(&c->l);
+
+			sshlog(c, "key exchange failure");
+			threadexits(nil);
+		}
+		add_byte(p2, SSH_MSG_NEWKEYS);
+		n = finish_packet(p2);
+		iowrite(c->rio, c->datafd, p2->nlength, n);
+		qlock(&c->l);
+		rwakeup(&c->r);
+		qunlock(&c->l);
+		break;
+	case SSH_MSG_SERVICE_REQUEST:
+		get_string(p, p->payload + 1, buf, Arbbufsz, nil);
+		sshdebug(c, "got service request: %s", buf);
+		if (strcmp(buf, "ssh-userauth") == 0 ||
+		    strcmp(buf, "ssh-connection") == 0) {
+			init_packet(p2);
+			p2->c = c;
+			sshdebug(c, "connection");
+			add_byte(p2, SSH_MSG_SERVICE_ACCEPT);
+			add_string(p2, buf);
+			n = finish_packet(p2);
+			iowrite(c->rio, c->datafd, p2->nlength, n);
+			c->state = Authing;
+		} else{
+			init_packet(p2);
+			p2->c = c;
+			add_byte(p2, SSH_MSG_DISCONNECT);
+			add_byte(p2, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE);
+			add_string(p2, "Unknown service type");
+			add_string(p2, "");
+			n = finish_packet(p2);
+			iowrite(c->rio, c->datafd, p2->nlength, n);
+			return -1;
+		}
+		break;
+	case SSH_MSG_SERVICE_ACCEPT:
+		get_string(p, p->payload + 1, buf, Arbbufsz, nil);
+		if (c->service && strcmp(c->service, "ssh-userauth") == 0) {
+			free(c->service);
+			c->service = estrdup9p("ssh-connection");
+		}
+		sshdebug(c, "got service accept: %s: responding with %s %s",
+			buf, c->user, c->service);
+		n = client_auth(c, c->rio);
+		c->state = Authing;
+		if (n < 0) {
+			qlock(&c->l);
+			rwakeup(&c->r);
+			qunlock(&c->l);
+		}
+		break;
+	}
+	return 0;
+}
+
+static void
+nochans(Conn *c, Packet *p, Packet *p2)
+{
+	int n;
+
+	init_packet(p2);
+	p2->c = c;
+	add_byte(p2, SSH_MSG_CHANNEL_OPEN_FAILURE);
+	add_block(p2, p->payload + 5, 4);
+	hnputl(p2->payload + p2->rlength - 1, 4);
+	p2->rlength += 4;
+	add_string(p2, "No available channels");
+	add_string(p2, "EN");
+	n = finish_packet(p2);
+	iowrite(c->rio, c->datafd, p2->nlength, n);
+}
+
+static int
+established(Conn *c, Packet *p, Packet *p2, char *buf, int size)
+{
+	int i, n, cnum;
+	uchar *q;
+	Plist *pl;
+	SSHChan *ch;
+
+	USED(size);
+	if (debug > 1) {
+		sshdebug(c, "in Established state, got:");
+		dump_packet(p);
+	}
+	switch (p->payload[0]) {
+	case SSH_MSG_DISCONNECT:
+		if (debug) {
+			get_string(p, p->payload + 5, buf, Arbbufsz, nil);
+			sshdebug(c, "got disconnect: %s", buf);
+		}
+		return -1;
+	case SSH_MSG_IGNORE:
+	case SSH_MSG_UNIMPLEMENTED:
+		break;
+	case SSH_MSG_DEBUG:
+		if (debug || p->payload[1]) {
+			get_string(p, p->payload + 2, buf, Arbbufsz, nil);
+			sshdebug(c, "got debug message: %s", buf);
+		}
+		break;
+	case SSH_MSG_KEXINIT:
+		send_kexinit(c);
+		if (c->rkexinit)
+			free(c->rkexinit);
+		c->rkexinit = new_packet(c);
+		memmove(c->rkexinit, p, sizeof(Packet));
+		if (validatekex(c, p) < 0) {
+			sshdebug(c, "kex crypto algorithm mismatch (Established)");
+			return -1;
+		}
+		sshdebug(c, "using %s Kex algorithm and %s PKA",
+			kexes[c->kexalg]->name, pkas[c->pkalg]->name);
+		c->state = Negotiating;
+		break;
+	case SSH_MSG_GLOBAL_REQUEST:
+	case SSH_MSG_REQUEST_SUCCESS:
+	case SSH_MSG_REQUEST_FAILURE:
+		break;
+	case SSH_MSG_CHANNEL_OPEN:
+		q = get_string(p, p->payload + 1, buf, Arbbufsz, nil);
+		sshdebug(c, "searching for a listener for channel type %s", buf);
+		ch = alloc_chan(c);
+		if (ch == nil) {
+			nochans(c, p, p2);
+			break;
+		}
+
+		sshdebug(c, "alloced channel %d for listener", ch->id);
+		qlock(&c->l);
+		ch->otherid = nhgetl(q);
+		ch->twindow = nhgetl(q+4);
+		sshdebug(c, "got lock in channel open");
+		for (i = 0; i < c->nchan; ++i)
+			if (c->chans[i] && c->chans[i]->state == Listening &&
+			    c->chans[i]->ann &&
+			    strcmp(c->chans[i]->ann, buf) == 0)
+				break;
+		if (i >= c->nchan) {
+			sshdebug(c, "no listener: sleeping");
+			ch->state = Opening;
+			if (ch->ann)
+				free(ch->ann);
+			ch->ann = estrdup9p(buf);
+			sshdebug(c, "waiting for someone to announce %s", ch->ann);
+			rsleep(&ch->r);
+		} else{
+			sshdebug(c, "found listener on channel %d", ch->id);
+			c->chans[i]->waker = ch->id;
+			rwakeup(&c->chans[i]->r);
+		}
+		qunlock(&c->l);
+		break;
+	case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
+		cnum = nhgetl(p->payload + 1);
+		ch = c->chans[cnum];
+		qlock(&c->l);
+		ch->otherid = nhgetl(p->payload+5);
+		ch->twindow = nhgetl(p->payload+9);
+		rwakeup(&ch->r);
+		qunlock(&c->l);
+		break;
+	case SSH_MSG_CHANNEL_OPEN_FAILURE:
+		cnum = nhgetl(p->payload + 1);
+		ch = c->chans[cnum];
+		qlock(&c->l);
+		rwakeup(&ch->r);
+		qunlock(&c->l);
+		return -1;
+	case SSH_MSG_CHANNEL_WINDOW_ADJUST:
+		cnum = nhgetl(p->payload + 1);
+		ch = c->chans[cnum];
+		ch->twindow += nhgetl(p->payload + 5);
+		sshdebug(c, "new twindow for channel: %d: %lud", cnum, ch->twindow);
+		qlock(&ch->xmtlock);
+		rwakeup(&ch->xmtrendez);
+		qunlock(&ch->xmtlock);
+		break;
+	case SSH_MSG_CHANNEL_DATA:
+	case SSH_MSG_CHANNEL_EXTENDED_DATA:
+		cnum = nhgetl(p->payload + 1);
+		ch = c->chans[cnum];
+		pl = emalloc9p(sizeof(Plist));
+		pl->pack = emalloc9p(sizeof(Packet));
+		memmove(pl->pack, p, sizeof(Packet));
+		if (p->payload[0] == SSH_MSG_CHANNEL_DATA) {
+			pl->rem = nhgetl(p->payload + 5);
+			pl->st = pl->pack->payload + 9;
+		} else {
+			pl->rem = nhgetl(p->payload + 9);
+			pl->st = pl->pack->payload + 13;
+		}
+		pl->next = nil;
+		if (ch->dataq == nil)
+			ch->dataq = pl;
+		else
+			ch->datatl->next = pl;
+		ch->datatl = pl;
+		ch->inrqueue += pl->rem;
+		nbsendul(ch->inchan, 1);
+		break;
+	case SSH_MSG_CHANNEL_EOF:
+		cnum = nhgetl(p->payload + 1);
+		ch = c->chans[cnum];
+		if (ch->state != Closed && ch->state != Closing) {
+			ch->state = Eof;
+			nbsendul(ch->inchan, 1);
+			nbsendul(ch->reqchan, 1);
+		}
+		break;
+	case SSH_MSG_CHANNEL_CLOSE:
+		cnum = nhgetl(p->payload + 1);
+		ch = c->chans[cnum];
+		if (ch->state != Closed && ch->state != Closing) {
+			init_packet(p2);
+			p2->c = c;
+			add_byte(p2, SSH_MSG_CHANNEL_CLOSE);
+			hnputl(p2->payload + 1, ch->otherid);
+			p2->rlength += 4;
+			n = finish_packet(p2);
+			iowrite(c->rio, c->datafd, p2->nlength, n);
+		}
+		qlock(&c->l);
+		if (ch->state != Closed) {
+			ch->state = Closed;
+			rwakeup(&ch->r);
+			nbsendul(ch->inchan, 1);
+			nbsendul(ch->reqchan, 1);
+			chanclose(ch->inchan);
+			chanclose(ch->reqchan);
+		}
+		qunlock(&c->l);
+		for (i = 0; i < MAXCONN && (!c->chans[i] ||
+		    c->chans[i]->state == Empty || c->chans[i]->state == Closed);
+		    ++i)
+			;
+		if (i >= MAXCONN)
+			return -1;
+		break;
+	case SSH_MSG_CHANNEL_REQUEST:
+		cnum = nhgetl(p->payload + 1);
+		ch = c->chans[cnum];
+		sshdebug(c, "queueing channel request for channel: %d", cnum);
+		q = get_string(p, p->payload+5, buf, Arbbufsz, nil);
+		pl = emalloc9p(sizeof(Plist));
+		pl->pack = emalloc9p(sizeof(Packet));
+		n = snprint((char *)pl->pack->payload,
+			Maxpayload, "%s %c", buf, *q? 't': 'f');
+		sshdebug(c, "request message begins: %s",
+			(char *)pl->pack->payload);
+		memmove(pl->pack->payload + n, q + 1, p->rlength - (11 + (n-2)));
+		pl->rem = p->rlength - 11 + 2;
+		pl->st = pl->pack->payload;
+		pl->next = nil;
+		if (ch->reqq == nil)
+			ch->reqq = pl;
+		else
+			ch->reqtl->next = pl;
+		ch->reqtl = pl;
+		nbsendul(ch->reqchan, 1);
+		break;
+	case SSH_MSG_CHANNEL_SUCCESS:
+	case SSH_MSG_CHANNEL_FAILURE:
+	default:
+		break;
+	}
+	return 0;
+}
+
+static void
+bail(Conn *c, Packet *p, Packet *p2, char *sts)
+{
+	shutdown(c);
+	free(p);
+	free(p2);
+	if (c->rio) {
+		closeioproc(c->rio);
+		c->rio = nil;
+	}
+	c->rpid = -1;
+	threadexits(sts);
+}
+
+static void
+reader0(Conn *c, Packet *p, Packet *p2)
+{
+	int i, n, nl, np, nm, nb;
+	char buf[Arbbufsz];
+
+	nm = 0;
+	nb = 4;
+	if (c->decrypt != -1)
+		nb = cryptos[c->decrypt]->blklen;
+	sshdebug(c, "calling read for connection %d, state %d, nb %d, dc %d",
+		c->id, c->state, nb, c->decrypt);
+	if ((nl = ioreadn(c->rio, c->datafd, p->nlength, nb)) != nb) {
+		sshdebug(c, "reader for connection %d exiting, got %d: %r",
+			c->id, nl);
+		bail(c, p, p2, "reader exiting");
+	}
+	if (c->decrypt != -1)
+		cryptos[c->decrypt]->decrypt(c->deccs, p->nlength, nb);
+	p->rlength = nhgetl(p->nlength);
+	sshdebug(c, "got message length: %ld", p->rlength);
+	if (p->rlength > Maxpktpay) {
+		sshdebug(c, "absurd packet length: %ld, unrecoverable decrypt failure",
+			p->rlength);
+		bail(c, p, p2, "absurd packet length");
+	}
+	np = ioreadn(c->rio, c->datafd, p->nlength + nb, p->rlength + 4 - nb);
+	if (c->inmac != -1)
+		nm = ioreadn(c->rio, c->datafd, p->nlength + p->rlength + 4,
+			SHA1dlen);		/* SHA1dlen was magic 20 */
+	n = nl + np + nm;
+	if (debug) {
+		sshdebug(c, "got message of %d bytes %d padding", n, p->pad_len);
+		if (p->payload[0] > SSH_MSG_CHANNEL_OPEN) {
+			i = nhgetl(p->payload+1);
+			if (c->chans[i])
+				sshdebug(c, " for channel %d win %lud",
+					i, c->chans[i]->rwindow);
+			else
+				sshdebug(c, " for invalid channel %d", i);
+		}
+		sshdebug(c, " first byte: %d", p->payload[0]);
+	}
+	/* SHA1dlen was magic 20 */
+	if (np != p->rlength + 4 - nb || c->inmac != -1 && nm != SHA1dlen) {
+		sshdebug(c, "got EOF/error on connection read: %d %d %r", np, nm);
+		bail(c, p, p2, "error or eof");
+	}
+	p->tlength = n;
+	p->rlength = n - 4;
+	if (undo_packet(p) < 0) {
+		sshdebug(c, "bad packet in connection %d: exiting", c->id);
+		bail(c, p, p2, "bad packet");
+	}
+
+	if (c->state == Initting) {
+		if (p->payload[0] != SSH_MSG_KEXINIT) {
+			sshdebug(c, "missing KEX init packet: %d", p->payload[0]);
+			bail(c, p, p2, "bad kex");
+		}
+		if (c->rkexinit)
+			free(c->rkexinit);
+		c->rkexinit = new_packet(c);
+		memmove(c->rkexinit, p, sizeof(Packet));
+		if (validatekex(c, p) < 0) {
+			sshdebug(c, "kex crypto algorithm mismatch (Initting)");
+			bail(c, p, p2, "bad kex");
+		}
+		sshdebug(c, "using %s Kex algorithm and %s PKA",
+			kexes[c->kexalg]->name, pkas[c->pkalg]->name);
+		if (c->role == Client)
+			kexes[c->kexalg]->clientkex1(c, p);
+		c->state = Negotiating;
+	} else if (c->state == Negotiating) {
+		if (negotiating(c, p, p2, buf, sizeof buf) < 0)
+			bail(c, p, p2, "negotiating");
+	} else if (c->state == Authing) {
+		switch (p->payload[0]) {
+		case SSH_MSG_DISCONNECT:
+			if (debug) {
+				get_string(p, p->payload + 5, buf, Arbbufsz, nil);
+				sshdebug(c, "got disconnect: %s", buf);
+			}
+			bail(c, p, p2, "msg disconnect");
+		case SSH_MSG_USERAUTH_REQUEST:
+			switch (auth_req(p, c)) {
+			case 0:
+				establish(c);
+				break;
+			case -1:
+				break;
+			case -2:
+				bail(c, p, p2, "in userauth request");
+			}
+			break;
+		case SSH_MSG_USERAUTH_FAILURE:
+			qlock(&c->l);
+			rwakeup(&c->r);
+			qunlock(&c->l);
+			break;
+		case SSH_MSG_USERAUTH_SUCCESS:
+			establish(c);
+			break;
+		case SSH_MSG_USERAUTH_BANNER:
+			break;
+		}
+	} else if (c->state == Established) {
+		if (established(c, p, p2, buf, sizeof buf) < 0)
+			bail(c, p, p2, "from established state");
+	} else {
+		sshdebug(c, "connection %d in bad state, reader exiting", c->id);
+		bail(c, p, p2, "bad conn state");
+	}
+}
+
+void
+reader(void *a)
+{
+	Conn *c;
+	Packet *p, *p2;
+
+	threadsetname("reader");
+	c = a;
+	c->rpid = threadid();
+	sshdebug(c, "starting reader for connection %d, pid %d", c->id, c->rpid);
+	threadsetname("reader");
+	p = new_packet(c);
+	p2 = new_packet(c);
+	c->rio = ioproc();
+	for(;;)
+		reader0(c, p, p2);
+}
+
+int
+validatekex(Conn *c, Packet *p)
+{
+	if (c->role == Server)
+		return validatekexs(p);
+	else
+		return validatekexc(p);
+}
+
+int
+validatekexs(Packet *p)
+{
+	uchar *q;
+	char *toks[Maxtoks];
+	int i, j, n;
+	char *buf;
+
+	buf = emalloc9p(Bigbufsz);
+	q = p->payload + 17;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	sshdebug(nil, "received KEX algs: %s", buf);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (i = 0; i < n; ++i)
+		for (j = 0; j < nelem(kexes); ++j)
+			if (strcmp(toks[i], kexes[j]->name) == 0)
+				goto foundk;
+	sshdebug(nil, "kex algs not in kexes");
+	free(buf);
+	return -1;
+foundk:
+	p->c->kexalg = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	sshdebug(nil, "received host key algs: %s", buf);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (i = 0; i < n; ++i)
+		for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j)
+			if (strcmp(toks[i], pkas[j]->name) == 0)
+				goto foundpka;
+	sshdebug(nil, "host key algs not in pkas");
+	free(buf);
+	return -1;
+foundpka:
+	p->c->pkalg = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	sshdebug(nil, "received C2S crypto algs: %s", buf);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (i = 0; i < n; ++i)
+		for (j = 0; j < nelem(cryptos); ++j)
+			if (strcmp(toks[i], cryptos[j]->name) == 0)
+				goto foundc1;
+	sshdebug(nil, "c2s crypto algs not in cryptos");
+	free(buf);
+	return -1;
+foundc1:
+	p->c->ncscrypt = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	sshdebug(nil, "received S2C crypto algs: %s", buf);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (i = 0; i < n; ++i)
+		for (j = 0; j < nelem(cryptos); ++j)
+			if (strcmp(toks[i], cryptos[j]->name) == 0)
+				goto foundc2;
+	sshdebug(nil, "s2c crypto algs not in cryptos");
+	free(buf);
+	return -1;
+foundc2:
+	p->c->nsccrypt = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	sshdebug(nil, "received C2S MAC algs: %s", buf);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (i = 0; i < n; ++i)
+		for (j = 0; j < nelem(macnames); ++j)
+			if (strcmp(toks[i], macnames[j]) == 0)
+				goto foundm1;
+	sshdebug(nil, "c2s mac algs not in cryptos");
+	free(buf);
+	return -1;
+foundm1:
+	p->c->ncsmac = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	sshdebug(nil, "received S2C MAC algs: %s", buf);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (i = 0; i < n; ++i)
+		for (j = 0; j < nelem(macnames); ++j)
+			if (strcmp(toks[i], macnames[j]) == 0)
+				goto foundm2;
+	sshdebug(nil, "s2c mac algs not in cryptos");
+	free(buf);
+	return -1;
+foundm2:
+	p->c->nscmac = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	free(buf);
+	if (*q)
+		return 1;
+	return 0;
+}
+
+int
+validatekexc(Packet *p)
+{
+	uchar *q;
+	char *toks[Maxtoks];
+	int i, j, n;
+	char *buf;
+
+	buf = emalloc9p(Bigbufsz);
+	q = p->payload + 17;
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (j = 0; j < nelem(kexes); ++j)
+		for (i = 0; i < n; ++i)
+			if (strcmp(toks[i], kexes[j]->name) == 0)
+				goto foundk;
+	free(buf);
+	return -1;
+foundk:
+	p->c->kexalg = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j)
+		for (i = 0; i < n; ++i)
+			if (strcmp(toks[i], pkas[j]->name) == 0)
+				goto foundpka;
+	free(buf);
+	return -1;
+foundpka:
+	p->c->pkalg = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (j = 0; j < nelem(cryptos); ++j)
+		for (i = 0; i < n; ++i)
+			if (strcmp(toks[i], cryptos[j]->name) == 0)
+				goto foundc1;
+	free(buf);
+	return -1;
+foundc1:
+	p->c->ncscrypt = j;
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (j = 0; j < nelem(cryptos); ++j)
+		for (i = 0; i < n; ++i)
+			if (strcmp(toks[i], cryptos[j]->name) == 0)
+				goto foundc2;
+	free(buf);
+	return -1;
+foundc2:
+	p->c->nsccrypt = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (j = 0; j < nelem(macnames); ++j)
+		for (i = 0; i < n; ++i)
+			if (strcmp(toks[i], macnames[j]) == 0)
+				goto foundm1;
+	free(buf);
+	return -1;
+foundm1:
+	p->c->ncsmac = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	n = gettokens(buf, toks, nelem(toks), ",");
+	for (j = 0; j < nelem(macnames); ++j)
+		for (i = 0; i < n; ++i)
+			if (strcmp(toks[i], macnames[j]) == 0)
+				goto foundm2;
+	free(buf);
+	return -1;
+foundm2:
+	p->c->nscmac = j;
+
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	q = get_string(p, q, buf, Bigbufsz, nil);
+	free(buf);
+	return *q != 0;
+}
+
+int
+memrandom(void *p, int n)
+{
+	uchar *cp;
+
+	for (cp = (uchar*)p; n > 0; n--)
+		*cp++ = fastrand();
+	return 0;
+}
+
+/*
+ *  create a change uid capability
+ */
+char*
+mkcap(char *from, char *to)
+{
+	int fd, fromtosz;
+	char *cap, *key;
+	uchar rand[SHA1dlen], hash[SHA1dlen];
+
+	fd = open("#¤/caphash", OWRITE);
+	if (fd < 0)
+		sshlog(nil, "can't open #¤/caphash: %r");
+
+	/* create the capability */
+	fromtosz = strlen(from) + 1 + strlen(to) + 1;
+	cap = emalloc9p(fromtosz + sizeof(rand)*3 + 1);
+	snprint(cap, fromtosz + sizeof(rand)*3 + 1, "%s@%s", from, to);
+	memrandom(rand, sizeof(rand));
+	key = cap + fromtosz;
+	enc64(key, sizeof(rand)*3, rand, sizeof(rand));
+
+	/* hash the capability */
+	hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
+
+	/* give the kernel the hash */
+	key[-1] = '@';
+	sshdebug(nil, "writing `%.*s' to caphash", SHA1dlen, hash);
+	if (write(fd, hash, SHA1dlen) != SHA1dlen) {
+		close(fd);
+		free(cap);
+		return nil;
+	}
+	close(fd);
+	return cap;
+}
+
+/*
+ * ask keyfs (assumes we are on an auth server)
+ */
+static AuthInfo *
+keyfsauth(char *me, char *user, char *pw, char *key1, char *key2)
+{
+	int fd;
+	char path[Arbpathlen];
+	AuthInfo *ai;
+
+	if (passtokey(key1, pw) == 0)
+		return nil;
+
+	snprint(path, Arbpathlen, "/mnt/keys/%s/key", user);
+	if ((fd = open(path, OREAD)) < 0) {
+		werrstr("Invalid user %s", user);
+		return nil;
+	}
+	if (read(fd, key2, DESKEYLEN) != DESKEYLEN) {
+		close(fd);
+		werrstr("Password mismatch 1");
+		return nil;
+	}
+	close(fd);
+
+	if (memcmp(key1, key2, DESKEYLEN) != 0) {
+		werrstr("Password mismatch 2");
+		return nil;
+	}
+
+	ai = emalloc9p(sizeof(AuthInfo));
+	ai->cuid = estrdup9p(user);
+	ai->suid = estrdup9p(me);
+	ai->cap = mkcap(me, user);
+	ai->nsecret = 0;
+	ai->secret = (uchar *)estrdup9p("");
+	return ai;
+}
+
+static void
+userauthfailed(Packet *p2)
+{
+	add_byte(p2, SSH_MSG_USERAUTH_FAILURE);
+	add_string(p2, "password,publickey");
+	add_byte(p2, 0);
+}
+
+static int
+authreqpk(Packet *p, Packet *p2, Conn *c, char *user, uchar *q,
+	char *alg, char *blob, char *sig, char *service, char *me)
+{
+	int n, thisway, nblob, nsig;
+	char method[32];
+
+	sshdebug(c, "auth_req publickey for user %s", user);
+	thisway = *q == '\0';
+	q = get_string(p, q+1, alg, Arbpathlen, nil);
+	q = get_string(p, q, blob, Blobsz, &nblob);
+	if (thisway) {
+		/*
+		 * Should really check to see if this user can
+		 * be authed this way.
+		 */
+		for (n = 0; n < nelem(pkas) && pkas[n] != nil &&
+		    strcmp(pkas[n]->name, alg) != 0; ++n)
+			;
+		if (n >= nelem(pkas) || pkas[n] == nil) {
+			userauthfailed(p2);
+			return -1;
+		}
+		add_byte(p2, SSH_MSG_USERAUTH_PK_OK);
+		add_string(p2, alg);
+		add_block(p2, blob, nblob);
+		return 0;
+	}
+
+	get_string(p, q, sig, Blobsz, &nsig);
+	for (n = 0; n < nelem(pkas) && pkas[n] != nil &&
+	    strcmp(pkas[n]->name, alg) != 0; ++n)
+		;
+	if (n >= nelem(pkas) || pkas[n] == nil) {
+		userauthfailed(p2);
+		return -1;
+	}
+
+	add_block(p2, c->sessid, SHA1dlen);
+	add_byte(p2, SSH_MSG_USERAUTH_REQUEST);
+	add_string(p2, user);
+	add_string(p2, service);
+	add_string(p2, method);
+	add_byte(p2, 1);
+	add_string(p2, alg);
+	add_block(p2, blob, nblob);
+	if (pkas[n]->verify(c, p2->payload, p2->rlength - 1, user, sig, nsig)
+	    == 0) {
+		init_packet(p2);
+		p2->c = c;
+		sshlog(c, "public key login failed");
+		userauthfailed(p2);
+		return -1;
+	}
+	free(c->cap);
+	c->cap = mkcap(me, user);
+	init_packet(p2);
+	p2->c = c;
+	sshlog(c, "logged in by public key");
+	add_byte(p2, SSH_MSG_USERAUTH_SUCCESS);
+	return 0;
+}
+
+int
+auth_req(Packet *p, Conn *c)
+{
+	int n, ret;
+	char *alg, *blob, *sig, *service, *me, *user, *pw, *path;
+	char key1[DESKEYLEN], key2[DESKEYLEN], method[32];
+	uchar *q;
+	AuthInfo *ai;
+	Packet *p2;
+
+	service = emalloc9p(Arbpathlen);
+	me = emalloc9p(Arbpathlen);
+	user = emalloc9p(Arbpathlen);
+	pw = emalloc9p(Arbpathlen);
+	alg = emalloc9p(Arbpathlen);
+	path = emalloc9p(Arbpathlen);
+	blob = emalloc9p(Blobsz);
+	sig = emalloc9p(Blobsz);
+	ret = -1;				/* failure is default */
+
+	q = get_string(p, p->payload + 1, user, Arbpathlen, nil);
+	free(c->user);
+	c->user = estrdup9p(user);
+	q = get_string(p, q, service, Arbpathlen, nil);
+	q = get_string(p, q, method, sizeof method, nil);
+	sshdebug(c, "got userauth request: %s %s %s", user, service, method);
+
+	readfile("/dev/user", me, Arbpathlen);
+
+	p2 = new_packet(c);
+	if (strcmp(method, "publickey") == 0)
+		ret = authreqpk(p, p2, c, user, q, alg, blob, sig, service, me);
+	else if (strcmp(method, "password") == 0) {
+		get_string(p, q + 1, pw, Arbpathlen, nil);
+		// sshdebug(c, "%s", pw);	/* bad idea to log passwords */
+		sshdebug(c, "auth_req password");
+		if (kflag)
+			ai = keyfsauth(me, user, pw, key1, key2);
+		else
+			ai = auth_userpasswd(user, pw);
+		if (ai == nil) {
+			sshlog(c, "login failed: %r");
+			userauthfailed(p2);
+		} else {
+			sshdebug(c, "auth successful: cuid %s suid %s cap %s",
+				ai->cuid, ai->suid, ai->cap);
+			free(c->cap);
+			if (strcmp(user, me) == 0)
+				c->cap = estrdup9p("n/a");
+			else
+				c->cap = estrdup9p(ai->cap);
+			sshlog(c, "logged in by password");
+			add_byte(p2, SSH_MSG_USERAUTH_SUCCESS);
+			auth_freeAI(ai);
+			ret = 0;
+		}
+	} else
+		userauthfailed(p2);
+
+	n = finish_packet(p2);
+	iowrite(c->dio, c->datafd, p2->nlength, n);
+
+	free(service);
+	free(me);
+	free(user);
+	free(pw);
+	free(alg);
+	free(blob);
+	free(sig);
+	free(path);
+	free(p2);
+	return ret;
+}
+
+int
+client_auth(Conn *c, Ioproc *io)
+{
+	Packet *p2, *p3, *p4;
+	char *r, *s;
+	mpint *ek, *nk;
+	int i, n;
+
+	sshdebug(c, "client_auth");
+	if (!c->password && !c->authkey)
+		return -1;
+
+	p2 = new_packet(c);
+	add_byte(p2, SSH_MSG_USERAUTH_REQUEST);
+	add_string(p2, c->user);
+	add_string(p2, c->service);
+	if (c->password) {
+		add_string(p2, "password");
+		add_byte(p2, 0);
+		add_string(p2, c->password);
+		sshdebug(c, "client_auth using password for svc %s", c->service);
+	} else {
+		sshdebug(c, "client_auth trying rsa public key");
+		add_string(p2, "publickey");
+		add_byte(p2, 1);
+		add_string(p2, "ssh-rsa");
+
+		r = strstr(c->authkey, " ek=");
+		s = strstr(c->authkey, " n=");
+		if (!r || !s) {
+			shutdown(c);
+			free(p2);
+			sshdebug(c, "client_auth no rsa key");
+			return -1;
+		}
+		ek = strtomp(r+4, nil, 16, nil);
+		nk = strtomp(s+3, nil, 16, nil);
+
+		p3 = new_packet(c);
+		add_string(p3, "ssh-rsa");
+		add_mp(p3, ek);
+		add_mp(p3, nk);
+		add_block(p2, p3->payload, p3->rlength-1);
+
+		p4 = new_packet(c);
+		add_block(p4, c->sessid, SHA1dlen);
+		add_byte(p4, SSH_MSG_USERAUTH_REQUEST);
+		add_string(p4, c->user);
+		add_string(p4, c->service);
+		add_string(p4, "publickey");
+		add_byte(p4, 1);
+		add_string(p4, "ssh-rsa");
+		add_block(p4, p3->payload, p3->rlength-1);
+		mpfree(ek);
+		mpfree(nk);
+		free(p3);
+
+		for (i = 0; pkas[i] && strcmp("ssh-rsa", pkas[i]->name) != 0;
+		    ++i)
+			;
+		sshdebug(c, "client_auth rsa signing alg %d: %r",  i);
+		if ((p3 = pkas[i]->sign(c, p4->payload, p4->rlength-1)) == nil) {
+			sshdebug(c, "client_auth rsa signing failed: %r");
+			free(p4);
+			free(p2);
+			return -1;
+		}
+		add_block(p2, p3->payload, p3->rlength-1);
+		free(p3);
+		free(p4);
+	}
+
+	n = finish_packet(p2);
+	if (writeio(io, c->datafd, p2->nlength, n) != n)
+		sshdebug(c, "client_auth write failed: %r");
+	free(p2);
+	return 0;
+}
+
+/* should use auth_getkey or something similar */
+char *
+factlookup(int nattr, int nreq, char *attrs[])
+{
+	Biobuf *bp;
+	char *buf, *toks[Maxtoks], *res, *q;
+	int ntok, nmatch, maxmatch;
+	int i, j;
+
+	res = nil;
+	bp = Bopen("/mnt/factotum/ctl", OREAD);
+	if (bp == nil)
+		return nil;
+	maxmatch = 0;
+	while (buf = Brdstr(bp, '\n', 1)) {
+		q = estrdup9p(buf);
+		ntok = gettokens(buf, toks, nelem(toks), " ");
+		nmatch = 0;
+		for (i = 0; i < nattr; ++i) {
+			for (j = 0; j < ntok; ++j)
+				if (strcmp(attrs[i], toks[j]) == 0) {
+					++nmatch;
+					break;
+				}
+			if (i < nreq && j >= ntok)
+				break;
+		}
+		if (i >= nattr && nmatch > maxmatch) {
+			free(res);
+			res = q;
+			maxmatch = nmatch;
+		} else
+			free(q);
+		free(buf);
+	}
+	Bterm(bp);
+	return res;
+}
+
+void
+shutdown(Conn *c)
+{
+	Plist *p;
+	SSHChan *sc;
+	int i, ostate;
+
+	sshdebug(c, "shutting down connection %d", c->id);
+	ostate = c->state;
+	if (c->clonefile->ref <= 2 && c->ctlfile->ref <= 2 &&
+	    c->datafile->ref <= 2 && c->listenfile->ref <= 2 &&
+	    c->localfile->ref <= 2 && c->remotefile->ref <= 2 &&
+	    c->statusfile->ref <= 2)
+		c->state = Closed;
+	else {
+		if (c->state != Closed)
+			c->state = Closing;
+		sshdebug(c, "clone %ld ctl %ld data %ld listen %ld "
+			"local %ld remote %ld status %ld",
+			c->clonefile->ref, c->ctlfile->ref, c->datafile->ref,
+			c->listenfile->ref, c->localfile->ref, c->remotefile->ref,
+			c->statusfile->ref);
+	}
+	if (ostate == Closed || ostate == Closing) {
+		c->state = Closed;
+		return;
+	}
+	if (c->role == Server && c->remote)
+		sshlog(c, "closing connection");
+	hangupconn(c);
+	if (c->dio) {
+		closeioproc(c->dio);
+		c->dio = nil;
+	}
+
+	c->decrypt = -1;
+	c->inmac = -1;
+	c->nchan = 0;
+	free(c->otherid);
+	free(c->s2ccs);
+	c->s2ccs = nil;
+	free(c->c2scs);
+	c->c2scs = nil;
+	free(c->remote);
+	c->remote = nil;
+	if (c->x) {
+		mpfree(c->x);
+		c->x = nil;
+	}
+	if (c->e) {
+		mpfree(c->e);
+		c->e = nil;
+	}
+	free(c->user);
+	c->user = nil;
+	free(c->service);
+	c->service = nil;
+	c->otherid = nil;
+	qlock(&c->l);
+	rwakeupall(&c->r);
+	qunlock(&c->l);
+	for (i = 0; i < MAXCONN; ++i) {
+		sc = c->chans[i];
+		if (sc == nil)
+			continue;
+		free(sc->ann);
+		sc->ann = nil;
+		if (sc->state != Empty && sc->state != Closed) {
+			sc->state = Closed;
+			sc->lreq = nil;
+			while (sc->dataq != nil) {
+				p = sc->dataq;
+				sc->dataq = p->next;
+				free(p->pack);
+				free(p);
+			}
+			while (sc->reqq != nil) {
+				p = sc->reqq;
+				sc->reqq = p->next;
+				free(p->pack);
+				free(p);
+			}
+			qlock(&c->l);
+			rwakeupall(&sc->r);
+			nbsendul(sc->inchan, 1);
+			nbsendul(sc->reqchan, 1);
+			chanclose(sc->inchan);
+			chanclose(sc->reqchan);
+			qunlock(&c->l);
+		}
+	}
+	qlock(&availlck);
+	rwakeup(&availrend);
+	qunlock(&availlck);
+	sshdebug(c, "done processing shutdown of connection %d", c->id);
+}

+ 351 - 0
sys/src/cmd/ssh2/netssh.h

@@ -0,0 +1,351 @@
+#include <bio.h>
+#include "ssh2.h"		/* ugh */
+
+#define MYID "SSH-2.0-Plan9"
+
+#pragma varargck type "M" mpint*
+
+enum {
+	Server =	0,
+	Client,
+
+	Maxpktpay =	35000,
+
+	/* qid.path components: level (2), type (4), conn (7), chan (7) */
+	Connshift =	7,
+	MAXCONN =	1 << Connshift,		/* also Maxchan */
+	Chanmask =	MAXCONN - 1,
+	Connmask =	Chanmask,
+
+	Qtypeshift =	2 * Connshift,		/* conn + chan */
+
+	Qroot =		0,
+	Qclone =	1 << Qtypeshift,
+	Qctl =		2 << Qtypeshift,
+	Qdata =		3 << Qtypeshift,
+	Qlisten =	4 << Qtypeshift,
+	Qlocal =	5 << Qtypeshift,
+	Qreqrem =	6 << Qtypeshift,	/* request or remote */
+	Qstatus =	7 << Qtypeshift,
+	Qtcp =		8 << Qtypeshift,
+	Qtypemask =	017 << Qtypeshift,
+
+	Levshift =	Qtypeshift + 4,
+
+	/* levels of /net/ssh hierarchy */
+	Top =		0,
+	Connection,
+	Subchannel,
+};
+
+/*
+ * The stylistic anomaly with these names of unbounded length
+ * is a result of following the RFCs in using the same names for
+ * these constants.  I did that to make it easier to search and
+ * cross-reference between the code and the RFCs.
+ */
+enum {					/* SSH2 Protocol Packet Types */
+	SSH_MSG_DISCONNECT =		1,
+	SSH_MSG_IGNORE =		2,
+	SSH_MSG_UNIMPLEMENTED,
+	SSH_MSG_DEBUG,
+	SSH_MSG_SERVICE_REQUEST,
+	SSH_MSG_SERVICE_ACCEPT,
+
+	SSH_MSG_KEXINIT =		20,
+	SSH_MSG_NEWKEYS,
+
+	SSH_MSG_KEXDH_INIT =		30,
+	SSH_MSG_KEXDH_REPLY,
+
+	SSH_MSG_USERAUTH_REQUEST =	50,
+	SSH_MSG_USERAUTH_FAILURE,
+	SSH_MSG_USERAUTH_SUCCESS,
+	SSH_MSG_USERAUTH_BANNER,
+
+	SSH_MSG_USERAUTH_PK_OK =	60,
+	SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60,
+
+	SSH_MSG_GLOBAL_REQUEST =	80,
+	SSH_MSG_REQUEST_SUCCESS,
+	SSH_MSG_REQUEST_FAILURE,
+
+	SSH_MSG_CHANNEL_OPEN =		90,
+	SSH_MSG_CHANNEL_OPEN_CONFIRMATION,
+	SSH_MSG_CHANNEL_OPEN_FAILURE,
+	SSH_MSG_CHANNEL_WINDOW_ADJUST,
+	SSH_MSG_CHANNEL_DATA,
+	SSH_MSG_CHANNEL_EXTENDED_DATA,
+	SSH_MSG_CHANNEL_EOF,
+	SSH_MSG_CHANNEL_CLOSE,
+	SSH_MSG_CHANNEL_REQUEST,
+	SSH_MSG_CHANNEL_SUCCESS,
+	SSH_MSG_CHANNEL_FAILURE,
+};
+
+enum {				/* SSH2 reason codes */
+	SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1,
+	SSH_DISCONNECT_PROTOCOL_ERROR,
+	SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+	SSH_DISCONNECT_RESERVED,
+	SSH_DISCONNECT_MAC_ERROR,
+	SSH_DISCONNECT_COMPRESSION_ERROR,
+	SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+	SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
+	SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE,
+	SSH_DISCONNECT_CONNECTION_LOST,
+	SSH_DISCONNECT_BY_APPLICATION,
+	SSH_DISCONNECT_TOO_MANY_CONNECTIONS,
+	SSH_DISCONNECT_AUTH_CANCELLED_BY_USER,
+	SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+	SSH_DISCONNECT_ILLEGAL_USR_NAME,
+
+	SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1,
+	SSH_OPEN_CONNECT_FAILED,
+	SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
+	SSH_OPEN_RESOURCE_SHORTAGE,
+};
+
+enum {				/* SSH2 type code */
+	SSH_EXTENDED_DATA_STDERR = 1,
+};
+
+enum {				/* connection and channel states */
+	Empty = 0,
+	Allocated,
+	Initting,
+	Listening,
+	Opening,
+	Negotiating,
+	Authing,
+	Established,
+	Eof,
+	Closing,
+	Closed,
+};
+
+enum {
+	NoKeyFile,
+	NoKey,
+	KeyWrong,
+	KeyOk,
+};
+
+typedef struct Cipher Cipher;
+typedef struct CipherState CipherState;
+typedef struct Conn Conn;
+typedef struct Kex Kex;
+typedef struct MBox MBox;
+typedef struct PKA PKA;
+typedef struct Packet Packet;
+typedef struct Plist Plist;
+typedef struct SSHChan SSHChan;
+
+#pragma incomplete CipherState
+
+struct Plist {
+	Packet	*pack;
+	uchar	*st;
+	int	rem;
+	Plist	*next;
+};
+
+struct SSHChan {
+	Rendez	r;			/* awaiting input? */
+	int	id;
+	int	otherid;
+	int	state;
+	int	waker;
+	int	conn;
+	ulong	rwindow;
+	ulong	twindow;
+	ulong	sent;
+	ulong	inrqueue;
+	char	*ann;
+	Req	*lreq;
+
+	/* File* for each Qid type */
+	File	*dir;
+	File	*ctl;
+	File	*data;
+	File	*listen;
+	File	*request;
+	File	*status;
+	File	*tcp;
+
+	Plist	*dataq;
+	Plist	*datatl;
+	Plist	*reqq;
+	Plist	*reqtl;
+
+	Channel	*inchan;
+	Channel	*reqchan;
+	QLock	xmtlock;
+	Rendez	xmtrendez;
+};
+
+struct Conn {
+	QLock	l;
+	Rendez	r;			/* awaiting input? */
+
+	Ioproc	*dio;
+	Ioproc	*cio;
+	Ioproc	*rio;
+
+	int	state;
+	int	role;
+	int	id;
+
+	char	*remote;
+	char	*user;
+	char	*password;
+
+	char	*service;
+	char	*cap;
+	char	*authkey;
+	int	nchan;
+
+	/* underlying tcp connection */
+	int	datafd;
+	int	ctlfd;
+	int	stifle;		/* flag: no i/o between listen and sshsession */
+	int	poisoned;
+	int	tcpconn;
+
+	int	rpid;
+
+	int	inseq;
+	int	outseq;
+	int	kexalg;
+	int	pkalg;
+
+	int	cscrypt;
+	int	ncscrypt;
+	int	sccrypt;
+	int	nsccrypt;
+
+	int	csmac;
+	int	ncsmac;
+	int	scmac;
+	int	nscmac;
+
+	int	encrypt;
+	int	decrypt;
+
+	int	outmac;
+	int	inmac;
+
+	/* File* for each Qid type */
+	File	*dir;
+	File	*clonefile;
+	File	*ctlfile;
+	File	*datafile;
+	File	*listenfile;
+	File	*localfile;
+	File	*remotefile;
+	File	*statusfile;
+	File	*tcpfile;
+
+	Packet	*skexinit;
+	Packet	*rkexinit;
+	mpint	*x;
+	mpint	*e;
+	int	got_sessid;
+	uchar	sessid[SHA1dlen];
+
+	uchar	c2siv[SHA1dlen*2];
+	uchar	nc2siv[SHA1dlen*2];
+	uchar	s2civ[SHA1dlen*2];
+	uchar	ns2civ[SHA1dlen*2];
+
+	uchar	c2sek[SHA1dlen*2];
+	uchar	nc2sek[SHA1dlen*2];
+	uchar	s2cek[SHA1dlen*2];
+	uchar	ns2cek[SHA1dlen*2];
+
+	uchar	c2sik[SHA1dlen*2];
+	uchar	nc2sik[SHA1dlen*2];
+	uchar	s2cik[SHA1dlen*2];
+	uchar	ns2cik[SHA1dlen*2];
+
+	char	*otherid;
+	uchar	*inik;
+	uchar	*outik;
+	CipherState *s2ccs;
+	CipherState *c2scs;
+	CipherState *enccs;
+	CipherState *deccs;
+	SSHChan	*chans[MAXCONN];
+
+	char	idstring[256];		/* max allowed by SSH spec */
+};
+
+struct Packet {
+	Conn	*c;
+	ulong	rlength;
+	ulong	tlength;
+	uchar	nlength[4];
+	uchar	pad_len;
+	uchar	payload[Maxpktpay];
+};
+
+struct Cipher {
+	char	*name;
+	int	blklen;
+	CipherState *(*init)(Conn*, int);
+	void	(*encrypt)(CipherState*, uchar*, int);
+	void	(*decrypt)(CipherState*, uchar*, int);
+};
+
+struct Kex {
+	char	*name;
+	int	(*serverkex)(Conn *, Packet *);
+	int	(*clientkex1)(Conn *, Packet *);
+	int	(*clientkex2)(Conn *, Packet *);
+};
+
+struct PKA {
+	char	*name;
+	Packet	*(*ks)(Conn *);
+	Packet	*(*sign)(Conn *, uchar *, int);
+	int	(*verify)(Conn *, uchar *, int, char *, char *, int);
+};
+
+struct MBox {
+	Channel	*mchan;
+	char	*msg;
+	int	state;
+};
+
+extern Cipher cipheraes128, cipheraes192, cipheraes256;
+extern Cipher cipherblowfish, cipher3des, cipherrc4;
+extern int debug;
+extern int sshkeychan[];
+extern Kex dh1sha1, dh14sha1;
+extern MBox keymbox;
+extern PKA rsa_pka, dss_pka, *pkas[];
+
+/* pubkey.c */
+int appendkey(char *, char *, RSApub *);
+int findkey(char *, char *, RSApub *);
+RSApub *readpublickey(Biobuf *, char **);
+int replacekey(char *, char *, RSApub *);
+
+/* dh.c */
+void dh_init(PKA *[]);
+
+/* transport.c */
+void	add_block(Packet *, void *, int);
+void	add_byte(Packet *, char);
+void	add_mp(Packet *, mpint *);
+int	add_packet(Packet *, void *, int);
+void	add_string(Packet *, char *);
+void	add_uint32(Packet *, ulong);
+void	dump_packet(Packet *);
+int	finish_packet(Packet *);
+mpint	*get_mp(uchar *q);
+uchar	*get_string(Packet *, uchar *, char *, int, int *);
+ulong	get_uint32(Packet *, uchar **);
+void	init_packet(Packet *);
+Packet	*new_packet(Conn *);
+int	undo_packet(Packet *);

+ 226 - 0
sys/src/cmd/ssh2/pubkey.c

@@ -0,0 +1,226 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <ctype.h>
+#include <libsec.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "netssh.h"
+
+enum {
+	Arbsz =	256,
+};
+
+static int
+parsepubkey(char *s, RSApub *key, char **sp, int base)
+{
+	int n;
+	char *host, *p, *z;
+
+	z = nil;
+	n = strtoul(s, &p, 10);
+	host = nil;
+	if(n < Arbsz || !isspace(*p)){		/* maybe this is a host name */
+		host = s;
+		s = strpbrk(s, " \t");
+		if(s == nil)
+			return -1;
+		z = s;
+		*s++ = '\0';
+		s += strspn(s, " \t");
+
+		n = strtoul(s, &p, 10);
+		if(n < Arbsz || !isspace(*p)){
+			if(z)
+				*z = ' ';
+			return -1;
+		}
+	}
+
+	/* Arbsz is just a sanity check */
+	if((key->ek = strtomp(p, &p, base, nil)) == nil ||
+	    (key->n = strtomp(p, &p, base, nil)) == nil ||
+	    (*p != '\0' && !isspace(*p)) || mpsignif(key->n) < Arbsz) {
+		mpfree(key->ek);
+		mpfree(key->n);
+		key->ek = nil;
+		key->n = nil;
+		if(z)
+			*z = ' ';
+		return -1;
+	}
+	if(host == nil){
+		if(*p != '\0'){
+			p += strspn(p, " \t");
+			if(*p != '\0'){
+				host = emalloc9p(strlen(p)+1);
+				strcpy(host, p);
+			}
+		}
+		free(s);
+	}
+	*sp = host;
+	return 0;
+}
+
+RSApub*
+readpublickey(Biobuf *b, char **sp)
+{
+	char *s;
+	RSApub *key;
+
+	key = emalloc9p(sizeof(RSApub));
+	if(key == nil)
+		return nil;
+
+	for (; (s = Brdstr(b, '\n', 1)) != nil; free(s))
+		if(s[0] != '#'){
+			if(parsepubkey(s, key, sp, 10) == 0 ||
+			    parsepubkey(s, key, sp, 16) == 0)
+				return key;
+			fprint(2, "warning: skipping line '%s'; cannot parse\n",
+				s);
+		}
+	free(key);
+	return nil;
+}
+
+static int
+match(char *pattern, char *aliases)
+{
+	char *s, *snext, *a, *anext, *ae;
+
+	for(s = pattern; s && *s; s = snext){
+		if((snext = strchr(s, ',')) != nil)
+			*snext++ = '\0';
+		for(a = aliases; a && *a; a = anext){
+			if((anext = strchr(a, ',')) != nil){
+				ae = anext;
+				anext++;
+			}else
+				ae = a + strlen(a);
+			if(ae - a == strlen(s) && memcmp(s, a, ae - a) == 0)
+				return 0;
+		}
+	}
+	return 1;
+}
+
+int
+findkey(char *keyfile, char *host, RSApub *key)
+{
+	char *h;
+	Biobuf *b;
+	RSApub *k;
+	int res;
+
+	if ((b = Bopen(keyfile, OREAD)) == nil)
+		return NoKeyFile;
+
+	for (res = NoKey; res != KeyOk;) {
+		if ((k = readpublickey(b, &h)) == nil)
+			break;
+		if (match(h, host) == 0) {
+			if (mpcmp(k->n, key->n) == 0 &&
+			    mpcmp(k->ek, key->ek) == 0)
+				res = KeyOk;
+			else
+				res = KeyWrong;
+		}
+		free(h);
+		free(k->ek);
+		free(k->n);
+		free(k);
+	}
+	Bterm(b);
+	return res;
+}
+
+int
+replacekey(char *keyfile, char *host, RSApub *hostkey)
+{
+	int ret;
+	char *h, *nkey, *p;
+	Biobuf *br, *bw;
+	Dir *d, nd;
+	RSApub *k;
+
+	ret = -1;
+	d = nil;
+	nkey = smprint("%s.new", keyfile);
+	if(nkey == nil)
+		return -1;
+
+	if((br = Bopen(keyfile, OREAD)) == nil)
+		goto out;
+	if((bw = Bopen(nkey, OWRITE)) == nil){
+		Bterm(br);
+		goto out;
+	}
+
+	while((k = readpublickey(br, &h)) != nil){
+		if(match(h, host) != 0)
+			Bprint(bw, "%s %d %.10M %.10M\n",
+				h, mpsignif(k->n), k->ek, k->n);
+		free(h);
+		rsapubfree(k);
+	}
+	Bprint(bw, "%s %d %.10M %.10M\n", host, mpsignif(hostkey->n),
+		hostkey->ek, hostkey->n);
+	Bterm(bw);
+	Bterm(br);
+
+	d = dirstat(nkey);
+	if(d == nil){
+		fprint(2, "new key file disappeared?\n");
+		goto out;
+	}
+
+	p = strrchr(d->name, '.');
+	if(p == nil || strcmp(p, ".new") != 0){
+		fprint(2, "%s: new key file changed names? %s to %s\n",
+			argv0, nkey, d->name);
+		goto out;
+	}
+
+	*p = '\0';
+	nulldir(&nd);
+	nd.name = d->name;
+	if(remove(keyfile) < 0){
+		fprint(2, "%s: error removing %s: %r\n", argv0, keyfile);
+		goto out;
+	}
+	if(dirwstat(nkey, &nd) < 0){
+		fprint(2, "%s: error renaming %s to %s: %r\n",
+			argv0, nkey, d->name);
+		goto out;
+	}
+	ret = 0;
+out:
+	free(d);
+	free(nkey);
+	return ret;
+}
+
+int
+appendkey(char *keyfile, char *host, RSApub *key)
+{
+	int fd, ret;
+
+	ret = -1;
+	if((fd = open(keyfile, OWRITE)) < 0){
+		fd = create(keyfile, OWRITE, 0666);
+		if(fd < 0){
+			fprint(2, "%s: can't open nor create %s: %r\n",
+				argv0, keyfile);
+			return -1;
+		}
+	}
+	if(seek(fd, 0, 2) >= 0 &&
+	    fprint(fd, "%s %d %.10M %.10M\n", host, mpsignif(key->n),
+	     key->ek, key->n) >= 0)
+		ret = 0;
+	close(fd);
+	return ret;
+}

+ 66 - 0
sys/src/cmd/ssh2/rsa2ssh2.c

@@ -0,0 +1,66 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <auth.h>
+#include <authsrv.h>
+#include <libsec.h>
+#include "netssh.h"
+
+Cipher *cryptos[1];
+int debug;
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [file]\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	Packet *p;
+	char *ep, *np, *user;
+	mpint *e, *n;
+	int fd, m;
+	char key[Maxrpcbuf], encpub[Maxrpcbuf];
+
+	ARGBEGIN {
+	default:
+		usage();
+	} ARGEND
+	if (argc > 1)
+		usage();
+
+	user = getenv("user");
+	if (argc == 0)
+		fd = 0;
+	else {
+		fd = open(argv[0], OREAD);
+		if (fd < 0)
+			usage();
+	}
+	m = read(fd, key, Maxrpcbuf - 1);
+	close(fd);
+	key[m >= 0? m: 0] = 0;
+
+	ep = strstr(key, " ek=");
+	np = strstr(key, " n=");
+	if (ep == nil || np == nil)
+		sysfatal("bad key file\n");
+	e = strtomp(ep+4, nil, 16, nil);
+	n = strtomp(np+3, nil, 16, nil);
+	p = new_packet(nil);
+	add_string(p, "ssh-rsa");
+	add_mp(p, e);
+	add_mp(p, n);
+	if ((m = enc64(encpub, Maxrpcbuf, p->payload, p->rlength-1)) < 0)
+		sysfatal("base-64 encoding failed\n");
+	print("ssh-rsa ");
+	write(1, encpub, m);
+	if (user)
+		print(" %s\n", user);
+}

+ 587 - 0
sys/src/cmd/ssh2/ssh.c

@@ -0,0 +1,587 @@
+/*
+ * ssh - remote login via SSH v2
+ *	/net/ssh does most of the work; we copy bytes back and forth
+ */
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include "ssh2.h"
+
+int doauth(int, char *);
+int isatty(int);
+
+char *user, *remote;
+char *netdir = "/net";
+int debug = 0;
+
+static int stripcr = 0;
+static int mflag = 0;
+static int iflag = -1;
+static int nopw = 0, nopka = 0;
+static int chpid;
+static int reqfd, dfd1, cfd1, dfd2, cfd2, consfd, kconsfd, cctlfd, notefd, keyfd;
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-dkKmr] [-l user] [-n dir] [-z attr=val] addr "
+		"[cmd [args]]\n", argv0);
+	exits("usage");
+}
+
+/*
+ * this is probably overkill except writing "kill" to notefd;
+ * file descriptors are closed by the kernel upon exit.
+ */
+static void
+shutdown(void)
+{
+	if (cctlfd > 0) {
+		fprint(cctlfd, "rawoff");
+		close(cctlfd);
+	}
+	if (consfd > 0)
+		close(consfd);
+	if (reqfd > 0) {
+		fprint(reqfd, "close");
+		close(reqfd);
+	}
+	close(dfd2);
+	close(dfd1);
+	close(cfd2);
+	close(cfd1);
+
+	fprint(notefd, "kill");
+	close(notefd);
+}
+
+static void
+bail(char *sts)
+{
+	shutdown();
+	exits(sts);
+}
+
+int
+handler(void *, char *)
+{
+	char *nf;
+	int fd;
+
+	if (chpid) {
+		nf = esmprint("/proc/%d/note", chpid);
+		fd = open(nf, OWRITE);
+		fprint(fd, "interrupt");
+		close(fd);
+		free(nf);
+	}
+	shutdown();
+	return 1;
+}
+
+static void
+parseargs(void)
+{
+	int n;
+	char *p, *q;
+
+	q = strchr(remote, '@');
+	if (q != nil) {
+		user = remote;
+		*q++ = 0;
+		remote = q;
+	}
+
+	q = strchr(remote, '!');
+	if (q) {
+		n = q - remote;
+		netdir = malloc(n+1);
+		if (netdir == nil)
+			sysfatal("out of memory");
+		strncpy(netdir, remote, n+1);
+		netdir[n] = '\0';
+
+		p = strrchr(netdir, '/');
+		if (p == nil) {
+			free(netdir);
+			netdir = "/net";
+		} else if (strcmp(p+1, "ssh") == 0)
+			*p = '\0';
+		else
+			remote = esmprint("%s/ssh", netdir);
+	}
+
+}
+
+static void
+mounttunnel(char *srv)
+{
+	int fd;
+
+	if (debug)
+		fprint(2, "%s: mounting %s on /net\n", argv0, srv);
+	fd = open(srv, OREAD);
+	if (fd < 0) {
+		if (debug)
+			fprint(2, "%s: can't open %s: %r\n", argv0, srv);
+	} else if (mount(fd, -1, "/net", MBEFORE, "") < 0)
+		sysfatal("can't mount in /net: %r");
+}
+
+static void
+newtunnel(char *myname)
+{
+	int kid, pid;
+
+	if(debug)
+		fprint(2, "%s: starting new netssh for key access\n", argv0);
+	kid = rfork(RFPROC|RFNOTEG|RFENVG /* |RFFDG */);
+	if (kid == 0) {
+//		for (fd = 3; fd < 40; fd++)
+//			close(fd);
+		execl("/bin/netssh", "netssh", "-m", netdir, "-s", myname, nil);
+		sysfatal("no /bin/netssh: %r");
+	} else if (kid < 0)
+		sysfatal("fork failed: %r");
+	while ((pid = waitpid()) != kid && pid >= 0)
+		;
+}
+
+static void
+starttunnel(void)
+{
+	char *keys, *mysrv, *myname;
+
+	keys = esmprint("%s/ssh/keys", netdir);
+	myname = esmprint("ssh.%s", getuser());
+	mysrv = esmprint("/srv/%s", myname);
+
+	if (access(keys, ORDWR) < 0)
+		mounttunnel("/srv/netssh");		/* old name */
+	if (access(keys, ORDWR) < 0)
+		mounttunnel("/srv/ssh");
+	if (access(keys, ORDWR) < 0)
+		mounttunnel(mysrv);
+	if (access(keys, ORDWR) < 0)
+		newtunnel(myname);
+	if (access(keys, ORDWR) < 0)
+		mounttunnel(mysrv);
+
+	/* if we *still* can't see our own tunnel, throw a tantrum. */
+	if (access(keys, ORDWR) < 0)
+		sysfatal("%s inaccessible: %r", keys);		/* WTF? */
+
+	free(myname);
+	free(mysrv);
+	free(keys);
+}
+
+int
+cmdmode(void)
+{
+	int n, m;
+	char buf[Arbbufsz];
+
+	for(;;) {
+reprompt:
+		print("\n>>> ");
+		n = 0;
+		do {
+			m = read(0, buf + n, sizeof buf - n - 1);
+			if (m <= 0)
+				return 1;
+			write(1, buf + n, m);
+			n += m;
+			buf[n] = '\0';
+			if (buf[n-1] == ('u' & 037))
+				goto reprompt;
+		} while (buf[n-1] != '\n' && buf[n-1] != '\r');
+		switch (buf[0]) {
+		case '\n':
+		case '\r':
+			break;
+		case 'q':
+			return 1;
+		case 'c':
+			return 0;
+		case 'r':
+			stripcr = !stripcr;
+			return 0;
+		case 'h':
+			print("c - continue\n");
+			print("h - help\n");
+			print("q - quit\n");
+			print("r - toggle carriage return stripping\n");
+			break;
+		default:
+			print("unknown command\n");
+			break;
+		}
+	}
+}
+
+static void
+keyprompt(char *buf, int size, int n)
+{
+	if (*buf == 'c') {
+		fprint(kconsfd, "The following key has been offered by the server:\n");
+		write(kconsfd, buf+5, n);
+		fprint(kconsfd, "\n\n");
+		fprint(kconsfd, "Add this key? (yes, no, session) ");
+	} else {
+		fprint(kconsfd, "The following key does NOT match the known "
+			"key(s) for the server:\n");
+		write(kconsfd, buf+5, n);
+		fprint(kconsfd, "\n\n");
+		fprint(kconsfd, "Add this key? (yes, no, session, replace) ");
+	}
+	n = read(kconsfd, buf, size - 1);
+	if (n <= 0)
+		return;
+	write(keyfd, buf, n);		/* user's response -> /net/ssh/keys */
+	seek(keyfd, 0, 2);
+	if (readn(keyfd, buf, 5) <= 0)
+		return;
+	buf[5] = 0;
+	n = strtol(buf+1, nil, 10);
+	n = readn(keyfd, buf+5, n);
+	if (n <= 0)
+		return;
+	buf[n+5] = 0;
+
+	switch (*buf) {
+	case 'b':
+	case 'f':
+		fprint(kconsfd, "%s\n", buf+5);
+	case 'o':
+		close(keyfd);
+		close(kconsfd);
+	}
+}
+
+/* talk the undocumented /net/ssh/keys protcol */
+static void
+keyproc(char *buf, int size)
+{
+	int n;
+	char *p;
+
+	if (size < 6)
+		exits("keyproc buffer too small");
+	keyfd = -1;
+	p = esmprint("%s/ssh/keys", netdir);
+	keyfd = open(p, OREAD);
+	if (keyfd < 0) {
+		chpid = 0;
+		sysfatal("failed to open ssh keys in %s: %r", p);
+	}
+
+	kconsfd = open("/dev/cons", ORDWR);
+	if (kconsfd < 0)
+		nopw = 1;
+
+	buf[0] = 0;
+	n = read(keyfd, buf, 5);		/* reading /net/ssh/keys */
+	if (n < 0)
+		sysfatal("%s read: %r", p);
+	buf[5] = 0;
+	n = strtol(buf+1, nil, 10);
+	n = readn(keyfd, buf+5, n);
+	buf[n+5] = 0;
+	free(p);
+
+	switch (*buf) {
+	case 'f':
+		if (kconsfd >= 0)
+			fprint(kconsfd, "%s\n", buf+5);
+		/* fall through */
+	case 'o':
+		close(keyfd);
+		if (kconsfd >= 0)
+			close(kconsfd);
+		break;
+	default:
+		if (kconsfd >= 0)
+			keyprompt(buf, size, n);
+		else {
+			fprint(keyfd, "n");
+			close(keyfd);
+		}
+		break;
+	}
+	chpid = 0;
+	exits(nil);
+}
+
+/*
+ * start a subproc to copy from network to stdout
+ * while we copy from stdin to network.
+ */
+static void
+bidircopy(char *buf, int size)
+{
+	int i, n, lstart;
+	char *path, *p, *q;
+
+	rfork(RFNOTEG);
+	path = esmprint("/proc/%d/notepg", getpid());
+	notefd = open(path, OWRITE);
+
+	switch (rfork(RFPROC|RFMEM|RFNOWAIT)) {
+	case 0:
+		while ((n = read(dfd2, buf, size - 1)) > 0) {
+			if (!stripcr)
+				p = buf + n;
+			else
+				for (i = 0, p = buf, q = buf; i < n; ++i, ++q)
+					if (*q != '\r')
+						*p++ = *q;
+			if (p != buf)
+				write(1, buf, p-buf);
+		}
+		/*
+		 * don't bother; it will be obvious when the user's prompt
+		 * changes.
+		 *
+		 * fprint(2, "%s: Connection closed by server\n", argv0);
+		 */
+		break;
+	default:
+		lstart = 1;
+		while ((n = read(0, buf, size - 1)) > 0) {
+			if (!mflag && lstart && buf[0] == 0x1c)
+				if (cmdmode())
+					break;
+				else
+					continue;
+			lstart = (buf[n-1] == '\n' || buf[n-1] == '\r');
+			write(dfd2, buf, n);
+		}
+		/*
+		 * don't bother; it will be obvious when the user's prompt
+		 * changes.
+		 *
+		 * fprint(2, "%s: EOF on client side\n", argv0);
+		 */
+		break;
+	case -1:
+		fprint(2, "%s: fork error: %r\n", argv0);
+		break;
+	}
+
+	bail(nil);
+}
+
+static int
+connect(char *buf, int size)
+{
+	int nfd, n;
+	char *dir, *ds, *nf;
+
+	dir = esmprint("%s/ssh", netdir);
+	ds = netmkaddr(remote, dir, "22");		/* tcp port 22 is ssh */
+	free(dir);
+
+	dfd1 = dial(ds, nil, nil, &cfd1);
+	if (dfd1 < 0) {
+		fprint(2, "%s: dial conn %s: %r\n", argv0, ds);
+		if (chpid) {
+			nf = esmprint("/proc/%d/note", chpid);
+			nfd = open(nf, OWRITE);
+			fprint(nfd, "interrupt");
+			close(nfd);
+		}
+		exits("can't dial");
+	}
+
+	seek(cfd1, 0, 0);
+	n = read(cfd1, buf, size - 1);
+	buf[n >= 0? n: 0] = 0;
+	return atoi(buf);
+}
+
+static int
+chanconnect(int conn, char *buf, int size)
+{
+	int n;
+	char *path;
+
+	path = esmprint("%s/ssh/%d!session", netdir, conn);
+	dfd2 = dial(path, nil, nil, &cfd2);
+	if (dfd2 < 0) {
+		fprint(2, "%s: dial chan %s: %r\n", argv0, path);
+		bail("dial");
+	}
+	free(path);
+
+	n = read(cfd2, buf, size - 1);
+	buf[n >= 0? n: 0] = 0;
+	return atoi(buf);
+}
+
+static void
+remotecmd(int argc, char *argv[], int conn, int chan, char *buf, int size)
+{
+	int i;
+	char *path, *q;
+
+	path = esmprint("%s/ssh/%d/%d/request", netdir, conn, chan);
+	reqfd = open(path, OWRITE);
+	if (reqfd < 0)
+		bail("can't open request chan");
+	if (argc == 0)
+		if (readfile("/env/TERM", buf, size) < 0)
+			fprint(reqfd, "shell");
+		else
+			fprint(reqfd, "shell %s", buf);
+	else {
+		q = buf;
+		assert(size >= Bigbufsz);
+		for (i = 0; i < argc; ++i)
+			q = seprint(q, buf + Bigbufsz, " %s", argv[i]);
+		if (q < buf + Bigbufsz)
+			fprint(reqfd, "exec%s", buf);
+		else {
+			fprint(2, "%s: command too long\n", argv0);
+			fprint(reqfd, "close");
+			bail("cmd too long");
+		}
+	}
+}
+
+void
+main(int argc, char *argv[])
+{
+	char *whichkey;
+	int conn, chan, n;
+	char buf[Copybufsz];
+
+	reqfd = dfd1 = cfd1 = dfd2 = cfd2 = consfd = kconsfd = cctlfd =
+		notefd = keyfd = -1;
+	whichkey = nil;
+	ARGBEGIN {
+	case 'a':
+	case 'v':
+	case 'x':
+		break;
+	case 'd':
+		debug++;
+		break;
+	case 'I':
+		iflag = 0;
+		break;
+	case 'i':		/* Used by scp */
+		iflag = 1;
+		break;
+	case 'l':
+		user = EARGF(usage());
+		break;
+	case 'k':
+		nopka = 1;
+		break;
+	case 'K':
+		nopw = 1;
+		break;
+	case 'm':
+		mflag = 1;
+		break;
+	case 'n':
+		netdir = EARGF(usage());
+		break;
+	case 'r':
+		stripcr = 1;
+		break;
+	case 'z':
+		whichkey = EARGF(usage());
+		break;
+	default:
+		usage();
+	} ARGEND;
+	if (argc == 0)
+		usage();
+
+	if (iflag == -1)
+		iflag = isatty(0);
+	remote = *argv++;
+	--argc;
+
+	parseargs();
+
+	if (!user)
+		user = getuser();
+	if (user == nil || remote == nil)
+		sysfatal("out of memory");
+
+	starttunnel();
+
+	/* fork subproc to handle keys; don't wait for it */
+	if ((n = rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT)) == 0)
+		keyproc(buf, sizeof buf);
+	chpid = n;
+	atnotify(handler,1);
+
+	/* connect and learn connection number */
+	conn = connect(buf, sizeof buf);
+
+	consfd = open("/dev/cons", ORDWR);
+	cctlfd = open("/dev/consctl", OWRITE);
+	fprint(cctlfd, "rawon");
+	if (doauth(cfd1, whichkey) < 0)
+		bail("doauth");
+
+	/* connect a channel of conn and learn channel number */
+	chan = chanconnect(conn, buf, sizeof buf);
+
+	/* open request channel, request shell or command execution */
+	remotecmd(argc, argv, conn, chan, buf, sizeof buf);
+
+	bidircopy(buf, sizeof buf);
+}
+
+int
+isatty(int fd)
+{
+	char buf[64];
+
+	buf[0] = '\0';
+	fd2path(fd, buf, sizeof buf);
+	return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
+}
+
+int
+doauth(int cfd1, char *whichkey)
+{
+	UserPasswd *up;
+	int n;
+	char path[Arbpathlen];
+
+ 	if (!nopka) {
+		if (whichkey)
+			n = fprint(cfd1, "ssh-userauth K %s %s", user, whichkey);
+		else
+			n = fprint(cfd1, "ssh-userauth K %s", user);
+		if (n >= 0)
+			return 0;
+	}
+	if (nopw)
+		return -1;
+	/*
+	up = auth_getuserpasswd(iflag? auth_getkey: nil,
+		"proto=pass service=ssh server=%q user=%q !password?",
+		remote, user);
+	 */
+	up = auth_getuserpasswd(iflag? auth_getkey: nil,
+		"proto=pass service=ssh server=%q user=%q", remote, user);
+	if (up == nil) {
+		fprint(2, "%s: didn't get password: %r\n", argv0);
+		return -1;
+	}
+	n = fprint(cfd1, "ssh-userauth k %s %s", user, up->passwd);
+	if (n >= 0)
+		return 0;
+	path[0] = '\0';
+	fd2path(cfd1, path, sizeof path);
+	fprint(2, "%s: auth ctl msg `ssh-userauth k %s <password>' for %s: %r\n",
+		argv0, user, path);
+	return -1;
+}

+ 29 - 0
sys/src/cmd/ssh2/ssh2.h

@@ -0,0 +1,29 @@
+enum {
+	Maxpayload =	32*1024,
+	Maxrpcbuf =	8192,		/* devmnt's MAXRPC - IOHDRSZ */
+	Copybufsz = 	4096,
+	Blobsz =	512,
+	Numbsz =	24,		/* enough digits for 2^64 */
+
+	Defstk =	80*1024,	/* was 8K, which seems small */
+	Maxtoks =	32,
+
+	Arbpathlen =	128,
+	Arbbufsz =	256,
+	Bigbufsz =	1024,
+	Maxfactotum =	256*1024,	/* max bytes in /mnt/factotum/ctl */
+};
+
+typedef struct Conn Conn;
+#pragma incomplete Conn
+
+#pragma	varargck argpos	esmprint 1
+#pragma	varargck argpos	ssdebug	2
+#pragma	varargck argpos	sshlog	2
+
+char *esmprint(char *format, ...);
+void sshdebug(Conn *, char *format, ...);
+void sshlog(Conn *, char *format, ...);
+
+void freeptr(void **);
+int readfile(char *file, char *buf, int size);

+ 497 - 0
sys/src/cmd/ssh2/sshsession.c

@@ -0,0 +1,497 @@
+/*
+ * ssh server - serve SSH protocol v2
+ *	/net/ssh does most of the work; we copy bytes back and forth
+ */
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <auth.h>
+#include "ssh2.h"
+
+char *confine(char *, char *);
+char *get_string(char *, char *);
+void newchannel(int, char *, int);
+void runcmd(int, int, char *, char *, char *, char *);
+
+int errfd, toppid, sflag, tflag, prevent;
+int debug;
+char *idstring;
+char *netdir = "/net";
+char *nsfile = nil;
+char *restdir;
+char *shell;
+char *srvpt;
+char *uname;
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-i id] [-s shell] [-r dir] [-R dir] [-S srvpt] "
+		"[-n ns] [-t] [netdir]\n", argv0);
+	exits("usage");
+}
+
+static int
+getctlfd(void)
+{
+	int ctlfd;
+	char *name;
+
+	name = smprint("%s/clone", netdir);
+	ctlfd = -1;
+	if (name)
+		ctlfd = open(name, ORDWR);
+	if (ctlfd < 0) {
+		syslog(0, "ssh", "server can't clone: %s: %r", name);
+		exits("open clone");
+	}
+	free(name);
+	return ctlfd;
+}
+
+static int
+getdatafd(int ctlfd)
+{
+	int fd;
+	char *name;
+
+	name = smprint("%s/data", netdir);
+	fd = -1;
+	if (name)
+		fd = open(name, OREAD);
+	if (fd < 0) {
+		syslog(0, "ssh", "can't open %s: %r", name);
+		hangup(ctlfd);
+		exits("open data");
+	}
+	free(name);
+	return fd;
+}
+
+static void
+auth(char *buf, int n, int ctlfd)
+{
+	int fd;
+
+	fd = open("#¤/capuse", OWRITE);
+	if (fd < 0) {
+		syslog(0, "ssh", "server can't open capuse: %r");
+		hangup(ctlfd);
+		exits("capuse");
+	}
+	if (write(fd, buf, n) != n) {
+		syslog(0, "ssh", "server write `%.*s' to capuse failed: %r",
+			n, buf);
+		hangup(ctlfd);
+		exits("capuse");
+	}
+	close(fd);
+}
+
+/*
+ * start tunnel if there isn't one, though it's probably too late
+ * since caphash will likely be closed.
+ */
+static void
+mounttunnel(int ctlfd)
+{
+	int fd;
+	char *p;
+
+	if (access(netdir, AEXIST) >= 0)
+		return;
+
+	p = smprint("/srv/%s", srvpt? srvpt: "ssh");
+	fd = -1;
+	if (p)
+		fd = open(p, ORDWR);
+	if (fd < 0) {
+		syslog(0, "ssh", "can't open %s: %r", p);
+		hangup(ctlfd);
+		exits("open");
+	}
+	if (mount(fd, -1, "/net", MBEFORE, "") < 0) {
+		syslog(0, "ssh", "can't mount in /net: %r");
+		hangup(ctlfd);
+		exits("can't mount");
+	}
+}
+
+static int
+authnewns(int ctlfd, char *buf, int size, int n)
+{
+	char *p, *q;
+
+	USED(size);
+	if (n <= 0)
+		return 0;
+	buf[n] = '\0';
+	if (strcmp(buf, "n/a") == 0)
+		return 0;
+
+	auth(buf, n, ctlfd);
+
+	p = strchr(buf, '@');
+	if (p == nil)
+		return 0;
+	++p;
+	q = strchr(p, '@');
+	if (q) {
+		*q = '\0';
+		uname = strdup(p);
+	}
+	if (!tflag && newns(p, nsfile) < 0) {
+		syslog(0, "ssh", "server: newns(%s,%s) failed: %r", p, nsfile);
+		return -1;
+	}
+	return 0;
+}
+
+static void
+listenloop(char *listfile, int ctlfd, char *buf, int size)
+{
+	int fd, n;
+
+	while ((fd = open(listfile, ORDWR)) >= 0) {
+		n = read(fd, buf, size - 1);
+		fprint(errfd, "read from listen file returned %d\n", n);
+		if (n <= 0) {
+			syslog(0, "ssh", "read on listen failed: %r");
+			break;
+		}
+		buf[n >= 0? n: 0] = '\0';
+		fprint(errfd, "read %s\n", buf);
+
+		switch (fork()) {
+		case 0:					/* child */
+			close(ctlfd);
+			newchannel(fd, netdir, atoi(buf));  /* never returns */
+		case -1:
+			syslog(0, "ssh", "fork failed: %r");
+			hangup(ctlfd);
+			exits("fork");
+		}
+		close(fd);
+	}
+	if (fd < 0)
+		syslog(0, "ssh", "listen failed: %r");
+}
+
+void
+main(int argc, char *argv[])
+{
+	char *listfile;
+	int ctlfd, fd, n;
+	char buf[Arbpathlen], path[Arbpathlen];
+
+	rfork(RFNOTEG);
+	toppid = getpid();
+	shell = "/bin/rc -il";
+	ARGBEGIN {
+	case 'd':
+		debug++;
+		break;
+	case 'i':
+		idstring = EARGF(usage());
+		break;
+	case 'n':
+		nsfile = EARGF(usage());
+		break;
+	case 'R':
+		prevent = 1;
+		/* fall through */
+	case 'r':
+		restdir = EARGF(usage());
+		break;
+	case 's':
+		sflag = 1;
+		shell = EARGF(usage());
+		break;
+	case 'S':
+		srvpt = EARGF(usage());
+		break;
+	case 't':
+		tflag = 1;
+		break;
+	default:
+		usage();
+		break;
+	} ARGEND;
+
+	errfd = -1;
+	if (debug)
+		errfd = 2;
+
+	/* work out network connection's directory */
+	if (argc >= 1)
+		netdir = argv[0];
+	else				/* invoked by listen1 */
+		netdir = getenv("net");
+	if (netdir == nil) {
+		syslog(0, "ssh", "server netdir is nil");
+		exits("nil netdir");
+	}
+	syslog(0, "ssh", "server netdir is %s", netdir);
+
+	uname = getenv("user");
+	if (uname == nil)
+		uname = "none";
+
+	/* extract dfd and cfd from netdir */
+	ctlfd = getctlfd();
+	fd = getdatafd(ctlfd);
+
+	n = read(fd, buf, sizeof buf - 1);
+	if (n < 0) {
+		syslog(0, "ssh", "server read error for data file: %r");
+		hangup(ctlfd);
+		exits("read cap");
+	}
+	close(fd);
+	authnewns(ctlfd, buf, sizeof buf, n);
+
+	/* get connection number in buf */
+	n = read(ctlfd, buf, sizeof buf - 1);
+	buf[n >= 0? n: 0] = '\0';
+
+	/* tell netssh our id string */
+	fd2path(ctlfd, path, sizeof path);
+	if (0 && idstring) {			/* was for coexistence */
+		syslog(0, "ssh", "server conn %s, writing \"id %s\" to %s",
+			buf, idstring, path);
+		fprint(ctlfd, "id %s", idstring);
+	}
+
+	/* announce */
+	fprint(ctlfd, "announce session");
+
+	/* construct listen file name */
+	listfile = smprint("%s/%s/listen", netdir, buf);
+	if (listfile == nil) {
+		syslog(0, "ssh", "out of memory");
+		exits("out of memory");
+	}
+	syslog(0, "ssh", "server listen is %s", listfile);
+
+	mounttunnel(ctlfd);
+	listenloop(listfile, ctlfd, buf, sizeof buf);
+	hangup(ctlfd);
+	exits(nil);
+}
+
+/* an abbreviation.  note the assumed variables. */
+#define REPLY(s)	if (want_reply) fprint(reqfd, s);
+
+static void
+forkshell(char *cmd, int reqfd, int datafd, int want_reply)
+{
+	switch (fork()) {
+	case 0:
+		if (sflag)
+			snprint(cmd, sizeof cmd, "-s%s", shell);
+		else
+			cmd[0] = '\0';
+		USED(cmd);
+		syslog(0, "ssh", "server starting ssh shell for %s", uname);
+		/* runcmd doesn't return */
+		runcmd(reqfd, datafd, "con", "/bin/ip/telnetd", "-nt", nil);
+	case -1:
+		REPLY("failure");
+		syslog(0, "ssh", "server can't fork: %r");
+		exits("fork");
+	}
+}
+
+static void
+forkcmd(char *cmd, char *p, int reqfd, int datafd, int want_reply)
+{
+	char *q;
+
+	switch (fork()) {
+	case 0:
+		if (restdir && chdir(restdir) < 0) {
+			syslog(0, "ssh", "can't chdir(%s): %r", restdir);
+			exits("can't chdir");
+		}
+		if (!prevent || (q = getenv("sshsession")) &&
+		    strcmp(q, "allow") == 0)
+			get_string(p+1, cmd);
+		else
+			confine(p+1, cmd);
+		syslog(0, "ssh", "server running `%s' for %s", cmd, uname);
+		/* runcmd doesn't return */
+		runcmd(reqfd, datafd, "rx", "/bin/rc", "-lc", cmd);
+	case -1:
+		REPLY("failure");
+		syslog(0, "ssh", "server can't fork: %r");
+		exits("fork");
+	}
+}
+
+void
+newchannel(int fd, char *conndir, int channum)
+{
+	char *p, *reqfile, *datafile;
+	int n, reqfd, datafd, want_reply, already_done;
+	char buf[Maxpayload], cmd[Bigbufsz];
+
+	close(fd);
+
+	already_done = 0;
+	reqfile = smprint("%s/%d/request", conndir, channum);
+	if (reqfile == nil)
+		sysfatal("out of memory");
+	reqfd = open(reqfile, ORDWR);
+	if (reqfd < 0) {
+		syslog(0, "ssh", "can't open request file %s: %r", reqfile);
+		exits("net");
+	}
+	datafile = smprint("%s/%d/data", conndir, channum);
+	if (datafile == nil)
+		sysfatal("out of memory");
+	datafd = open(datafile, ORDWR);
+	if (datafd < 0) {
+		syslog(0, "ssh", "can't open data file %s: %r", datafile);
+		exits("net");
+	}
+	while ((n = read(reqfd, buf, sizeof buf - 1)) > 0) {
+		fprint(errfd, "read from request file returned %d\n", n);
+		for (p = buf; p < buf + n && *p != ' '; ++p)
+			;
+		*p++ = '\0';
+		want_reply = (*p == 't');
+		/* shell, exec, and various flavours of failure */
+		if (strcmp(buf, "shell") == 0) {
+			if (already_done) {
+				REPLY("failure");
+				continue;
+			}
+			forkshell(cmd, reqfd, datafd, want_reply);
+			already_done = 1;
+			REPLY("success");
+		} else if (strcmp(buf, "exec") == 0) {
+			if (already_done) {
+				REPLY("failure");
+				continue;
+			}
+			forkcmd(cmd, p, reqfd, datafd, want_reply);
+			already_done = 1;
+			REPLY("success");
+		} else if (strcmp(buf, "pty-req") == 0 ||
+		    strcmp(buf, "window-change") == 0) {
+			REPLY("success");
+		} else if (strcmp(buf, "x11-req") == 0 ||
+		    strcmp(buf, "env") == 0 || strcmp(buf, "subsystem") == 0) {
+			REPLY("failure");
+		} else if (strcmp(buf, "xon-xoff") == 0 ||
+		    strcmp(buf, "signal") == 0 ||
+		    strcmp(buf, "exit-status") == 0 ||
+		    strcmp(buf, "exit-signal") == 0) {
+			;
+		} else
+			syslog(0, "ssh", "server unknown channel request: %s",
+				buf);
+	}
+	if (n < 0)
+		syslog(0, "ssh", "server read failed: %r");
+	exits(nil);
+}
+
+char *
+get_string(char *q, char *s)
+{
+	int n;
+
+	n = nhgetl(q);
+	q += 4;
+	memmove(s, q, n);
+	s[n] = '\0';
+	q += n;
+	return q;
+}
+
+char *
+confine(char *q, char *s)
+{
+	int i, n, m;
+	char *p, *e, *r, *buf, *toks[Maxtoks];
+
+	n = nhgetl(q);
+	q += 4;
+	buf = malloc(n+1);
+	if (buf == nil)
+		return nil;
+	memmove(buf, q, n);
+	buf[n]  = 0;
+	m = tokenize(buf, toks, nelem(toks));
+	e = s + n + 1;
+	for (i = 0, r = s; i < m; ++i) {
+		p = strrchr(toks[i], '/');
+		if (p == nil)
+			r = seprint(r, e, "%s ", toks[i]);
+		else if (p[0] != '\0' && p[1] != '\0')
+			r = seprint(r, e, "%s ", p+1);
+		else
+			r = seprint(r, e, ". ");
+	}
+	free(buf);
+	q += n;
+	return q;
+}
+
+void
+runcmd(int reqfd, int datafd, char *svc, char *cmd, char *arg1, char *arg2)
+{
+	char *p;
+	int fd, cmdpid, child;
+
+	cmdpid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFENVG);
+	switch (cmdpid) {
+	case -1:
+		syslog(0, "ssh", "fork failed: %r");
+		exits("fork");
+	case 0:
+		if (restdir == nil) {
+			p = smprint("/usr/%s", uname);
+			if (p && access(p, AREAD) == 0 && chdir(p) < 0) {
+				syslog(0, "ssh", "can't chdir(%s): %r", p);
+				exits("can't chdir");
+			}
+			free(p);
+		}
+		p = strrchr(cmd, '/');
+		if (p)
+			++p;
+		else
+			p = cmd;
+
+		dup(datafd, 0);
+		dup(datafd, 1);
+		dup(datafd, 2);
+		close(datafd);
+		putenv("service", svc);
+		fprint(errfd, "starting %s\n", cmd);
+		execl(cmd, p, arg1, arg2, nil);
+
+		syslog(0, "ssh", "cannot exec %s: %r", cmd);
+		exits("exec");
+	default:
+		close(datafd);
+		fprint(errfd, "waiting for child %d\n", cmdpid);
+		while ((child = waitpid()) != cmdpid && child != -1)
+			fprint(errfd, "child %d passed\n", child);
+		if (child == -1)
+			fprint(errfd, "wait failed: %r\n");
+
+		syslog(0, "ssh", "server closing ssh session for %s", uname);
+		fprint(errfd, "closing connection\n");
+		fprint(reqfd, "close");
+		p = smprint("/proc/%d/notepg", toppid);
+		if (p) {
+			fd = open(p, OWRITE);
+			fprint(fd, "interrupt");
+			close(fd);
+		}
+		exits(nil);
+	}
+}

+ 236 - 0
sys/src/cmd/ssh2/transport.c

@@ -0,0 +1,236 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <libsec.h>
+#include <ip.h>
+#include "netssh.h"
+
+extern Cipher *cryptos[];
+
+Packet *
+new_packet(Conn *c)
+{
+	Packet *p;
+
+	p = emalloc9p(sizeof(Packet));
+	init_packet(p);
+	p->c = c;
+	return p;
+}
+
+void
+init_packet(Packet *p)
+{
+	memset(p, 0, sizeof(Packet));
+	p->rlength = 1;
+}
+
+void
+add_byte(Packet *p, char c)
+{
+	p->payload[p->rlength-1] = c;
+	p->rlength++;
+}
+
+void
+add_uint32(Packet *p, ulong l)
+{
+	hnputl(p->payload+p->rlength-1, l);
+	p->rlength += 4;
+}
+
+ulong
+get_uint32(Packet *, uchar **data)
+{
+	ulong x;
+	x = nhgetl(*data);
+	*data += 4;
+	return x;
+}
+
+int
+add_packet(Packet *p, void *data, int len)
+{
+	if(p->rlength + len > Maxpayload)
+		return -1;
+	memmove(p->payload + p->rlength - 1, data, len);
+	p->rlength += len;
+	return 0;
+}
+
+void
+add_block(Packet *p, void *data, int len)
+{
+	hnputl(p->payload + p->rlength - 1, len);
+	p->rlength += 4;
+	add_packet(p, data, len);
+}
+
+void 
+add_string(Packet *p, char *s)
+{
+	uchar *q;
+	int n;
+	uchar nn[4];
+
+	n = strlen(s);
+	hnputl(nn, n);
+	q = p->payload + p->rlength - 1;
+	memmove(q, nn, 4);
+	memmove(q+4, s, n);
+	p->rlength += n + 4;
+}
+
+uchar *
+get_string(Packet *p, uchar *q, char *s, int lim, int *len)
+{
+	int n, m;
+
+	if (p && q > p->payload + p->rlength)
+		s[0] = '\0';
+	m = nhgetl(q);
+	q += 4;
+	if(m < lim)
+		n = m;
+	else
+		n = lim - 1;
+	memmove(s, q, n);
+	s[n] = '\0';
+	q += m;
+	if(len)
+		*len = n;
+	return q;
+}
+
+void
+add_mp(Packet *p, mpint *x)
+{
+	uchar *q;
+	int n;
+
+	q = p->payload + p->rlength - 1;
+	n = mptobe(x, q + 4, Maxpktpay - p->rlength + 1 - 4, nil);
+	if(q[4] & 0x80){
+		memmove(q + 5, q + 4, n);
+		q[4] = 0;
+		n++;
+	}
+	hnputl(q, n);
+	p->rlength += n + 4;
+}
+
+mpint *
+get_mp(uchar *q)
+{
+	return betomp(q + 4, nhgetl(q), nil);
+}
+
+int
+finish_packet(Packet *p)
+{
+	Conn *c;
+	uchar *q, *buf;
+	int blklen, i, n2, n1, maclen;
+
+	c = p->c;
+	blklen = 8;
+	if(c && debug > 1)
+		fprint(2, "%s: in finish_packet: enc %d outmac %d len %ld\n",
+			argv0, c->encrypt, c->outmac, p->rlength);
+	if(c && c->encrypt != -1){
+		blklen = cryptos[c->encrypt]->blklen;
+		if(blklen < 8)
+			blklen = 8;
+	}
+	n1 = p->rlength - 1;
+	n2 = blklen - (n1 + 5) % blklen;
+	if(n2 < 4)
+		n2 += blklen;
+	p->pad_len = n2;
+	for(i = 0, q = p->payload + n1; i < n2; ++i, ++q)
+		*q = fastrand();
+	p->rlength = n1 + n2 + 1;
+	hnputl(p->nlength, p->rlength);
+	maclen = 0;
+	if(c && c->outmac != -1){
+		maclen = SHA1dlen;
+		buf = emalloc9p(Maxpktpay);
+		hnputl(buf, c->outseq);
+		memmove(buf + 4, p->nlength, p->rlength + 4);
+		hmac_sha1(buf, p->rlength + 8, c->outik, maclen, q, nil);
+		free(buf);
+	}
+	if(c && c->encrypt != -1)
+		cryptos[c->encrypt]->encrypt(c->enccs, p->nlength, p->rlength + 4);
+	if (c)
+		c->outseq++;
+	if(debug > 1)
+		fprint(2, "%s: leaving finish packet: len %ld n1 %d n2 %d maclen %d\n",
+			argv0, p->rlength, n1, n2, maclen);
+	return p->rlength + 4 + maclen;
+}
+
+/*
+ * The first blklen bytes are already decrypted so we could find the
+ * length.
+ */
+int
+undo_packet(Packet *p)
+{
+	Conn *c;
+	long nlength;
+	int nb;
+	uchar rmac[SHA1dlen], *buf;
+
+	c = p->c;
+	nb = 4;
+	if(c->decrypt != -1)
+		nb = cryptos[c->decrypt]->blklen;
+	if(c->inmac != -1)
+		p->rlength -= SHA1dlen;			/* was magic 20 */
+	nlength = nhgetl(p->nlength);
+	if(c->decrypt != -1)
+		cryptos[c->decrypt]->decrypt(c->deccs, p->nlength + nb,
+			p->rlength + 4 - nb);
+	if(c->inmac != -1){
+		buf = emalloc9p(Maxpktpay);
+		hnputl(buf, c->inseq);
+		memmove(buf + 4, p->nlength, nlength + 4);
+		hmac_sha1(buf, nlength + 8, c->inik, SHA1dlen, rmac, nil);
+		free(buf);
+		if(memcmp(rmac, p->payload + nlength - 1, SHA1dlen) != 0){
+			fprint(2, "%s: received MAC verification failed: seq=%d\n",
+				argv0, c->inseq);
+			return -1;
+		}
+	}
+	c->inseq++;
+	p->rlength -= p->pad_len;
+	p->pad_len = 0;
+	return p->rlength - 1;
+}
+
+void
+dump_packet(Packet *p)
+{
+	int i;
+	char *buf, *q, *e;
+
+	fprint(2, "Length: %ld, Padding length: %d\n", p->rlength, p->pad_len);
+	q = buf = emalloc9p(Copybufsz);
+	e = buf + Copybufsz;
+	for(i = 0; i < p->rlength - 1; ++i){
+		q = seprint(q, e, " %02x", p->payload[i]);
+		if(i % 16 == 15)
+			q = seprint(q, e, "\n");
+		if(q - buf > Copybufsz - 4){
+			fprint(2, "%s", buf);
+			q = buf;
+		}
+	}
+	fprint(2, "%s\n", buf);
+	free(buf);
+}

+ 1 - 10
sys/src/libc/arm/getfcr.s

@@ -1,21 +1,12 @@
-/* for VFP */
-#define VMRS(fp, cpu) WORD $(0xeef00a10 | (fp)<<16 | (cpu)<<12) /* FP → arm */
-#define VMSR(cpu, fp) WORD $(0xeee00a10 | (fp)<<16 | (cpu)<<12) /* arm → FP */
-
-#define Fpscr 1
-
 TEXT	setfcr(SB), $0
-	VMSR(0, Fpscr)
 	RET
 
 TEXT	getfcr(SB), $0
-	VMRS(Fpscr, 0)
 	RET
 
 TEXT	getfsr(SB), $0
-	VMSR(0, Fpscr)
 	RET
 
 TEXT	setfsr(SB), $0
-	VMRS(Fpscr, 0)
 	RET
+

+ 21 - 0
sys/src/libc/arm/getfcr.vfp.S

@@ -0,0 +1,21 @@
+/* for VFP */
+#define VMRS(fp, cpu) WORD $(0xeef00a10 | (fp)<<16 | (cpu)<<12) /* FP → arm */
+#define VMSR(cpu, fp) WORD $(0xeee00a10 | (fp)<<16 | (cpu)<<12) /* arm → FP */
+
+#define Fpscr 1
+
+TEXT	setfcr(SB), $0
+	VMSR(0, Fpscr)
+	RET
+
+TEXT	getfcr(SB), $0
+	VMRS(Fpscr, 0)
+	RET
+
+TEXT	getfsr(SB), $0
+	VMSR(0, Fpscr)
+	RET
+
+TEXT	setfsr(SB), $0
+	VMRS(Fpscr, 0)
+	RET

Some files were not shown because too many files changed in this diff