示例#1
0
func (cc *ClientCtx) thrMain() {
	defer cc.Close()

	// thrMain only handles incoming segments
	for {
		select {
		case <-cc.cmn.deadch:
			return
		case seg := <-cc.cmn.dnch:
			switch seg.Flag {
			case kcOPEN:
				kilog.Warning("kiricom: severe protocol violation: kcOPEN on client")
				return

			case kcDATA:
				fallthrough
			case kcCLOS:
				cc.tablock.Lock()
				tgt, ok := cc.conntab[seg.ConnID]
				cc.tablock.Unlock()
				if !ok {
					kilog.FineDebug("kiricom: %v to invalid connid", seg)
					continue
				}
				select {
				case tgt.inbox <- seg:
				default:
					kilog.Warning("kiricom: severe protocol violation: overfull flow control buffer")
					return
				}
				if seg.Flag == kcCLOS {
					cc.tablock.Lock()
					delete(cc.conntab, seg.ConnID)
					cc.tablock.Unlock()
				}

			case kcMORE:
				cc.tablock.Lock()
				tgt, ok := cc.conntab[seg.ConnID]
				cc.tablock.Unlock()
				if !ok {
					kilog.FineDebug("kiricom: %v to invalid connid", seg)
					continue
				}
				select {
				case tgt.morech <- true:
					kilog.FineDebug("kiricom: sent MORE to connid %v", tgt.connid)
				default:
					kilog.Warning("kiricom: severe protocol violation: duplicate MORE")
					return
				}
			}
		}
	}
}
示例#2
0
// LLObfsServerHandshake negotiates low-level obfuscation (content hiding but no
// volume hiding) on a network connection, acting as the server. The master
// secret must be provided.
func LLObfsServerHandshake(secret []byte, transport io.ReadWriteCloser) (io.ReadWriteCloser, error) {
	// Client needs to send proof that they actually have our secret
	proof := make([]byte, 64)
	_, err := io.ReadFull(transport, proof)
	if err != nil {
		return nil, err
	}
	kilog.FineDebug("llobfs: server obtained proof")
	// We need to verify proof
	nonce := proof[:32]
	hash := proof[32:]
	if subtle.ConstantTimeCompare(natrium.SecureHash(secret, nonce), hash) != 1 {
		return nil, errors.New("Client did not give the right proof")
	}
	// Generate our ephemeral keys
	our_keys := UDHGenerateKeys()

	// Send our public key
	_, err = transport.Write(our_keys.Public)
	if err != nil {
		return nil, err
	}
	kilog.FineDebug("llobfs: server sent public key")

	// Read their public key
	their_public := make([]byte, 1536/8)
	_, err = io.ReadFull(transport, their_public)
	if err != nil {
		return nil, err
	}
	kilog.FineDebug("llobfs: server read public key")
	// Compute shared secret
	shared_secret := UDHSecret(our_keys.Private, their_public)
	// Read and write keys
	read_key := natrium.SecureHash(shared_secret, []byte("llobfs-upstream-key"))
	write_key := natrium.SecureHash(shared_secret, []byte("llobfs-downstream-key"))
	// Create struct
	toret := new(llobfsContext)

	toret.read_chug, _ = rc4.NewCipher(read_key)
	toret.write_chug, _ = rc4.NewCipher(write_key)

	dummy := make([]byte, 1536)
	toret.read_chug.XORKeyStream(dummy, dummy)
	toret.write_chug.XORKeyStream(dummy, dummy)

	toret.underlying = transport
	return toret, nil
}
示例#3
0
func (cc *commonCtx) thrProcessDown() {
	for {
		var seg kcSegment
		err := struc.Unpack(cc.carrier, &seg)
		if err != nil {
			cc.destroy(err)
			return
		}
		// if we get ping, respond with a pong
		if seg.Flag == kcPING {
			kilog.FineDebug("kiricom: commonCtx successfully received ping %v", seg)
			seg.Flag = kcPONG
			go func() {
				select {
				case cc.upch <- seg:
				case <-cc.deadch:
				}
			}()
			continue
		}
		// if we get pong, update latency value
		if seg.Flag == kcPONG {
			lat := (time.Now().UnixNano() - int64(binary.BigEndian.Uint64(seg.Body))) / (1000 * 1000)
			kilog.FineDebug("kiricom: commonCtx successfully received pong (%v ms)", lat)
			cc.latency = int(lat)
			continue
		}
		select {
		case cc.dnch <- seg:
			kilog.FineDebug("kiricom: commonCtx successfully received %v", seg)
			select {
			case cc.wdch <- true:
			case <-cc.deadch:
				return
			}
		case <-cc.deadch:
			return
		case <-time.After(time.Second):
			panic("this should NEVER happen because dnch should always be read from")
		}
	}
}
示例#4
0
// LLObfsClientHandshake negotiates low-level obfuscation as a client. The server
// secret must be given so that the client can prove knowledge.
func LLObfsClientHandshake(secret []byte, transport io.ReadWriteCloser) (io.ReadWriteCloser, error) {
	// Prove knowledge to client first
	nonce := make([]byte, 32)
	rand.Read(nonce)
	hash := natrium.SecureHash(secret, nonce)
	_, err := transport.Write(append(nonce, hash...))
	if err != nil {
		return nil, err
	}
	kilog.FineDebug("llobfs: client sent proof")
	// Read their public key
	their_public := make([]byte, 1536/8)
	_, err = io.ReadFull(transport, their_public)
	if err != nil {
		return nil, err
	}
	kilog.FineDebug("llobfs: client read server's public key")
	// Make our keys
	our_keys := UDHGenerateKeys()
	// Send our public key
	_, err = transport.Write(our_keys.Public)
	if err != nil {
		return nil, err
	}
	kilog.FineDebug("llobfs: client sent public key")
	// Compute shared secret
	shared_secret := UDHSecret(our_keys.Private, their_public)
	// Derive keys
	read_key := natrium.SecureHash(shared_secret, []byte("llobfs-downstream-key"))
	write_key := natrium.SecureHash(shared_secret, []byte("llobfs-upstream-key"))
	toret := new(llobfsContext)
	toret.read_chug, _ = rc4.NewCipher(read_key)
	toret.write_chug, _ = rc4.NewCipher(write_key)

	dummy := make([]byte, 1536)
	toret.read_chug.XORKeyStream(dummy, dummy)
	toret.write_chug.XORKeyStream(dummy, dummy)
	toret.underlying = transport

	return toret, nil
}
示例#5
0
// Push enqueues a segment into the jitter buffer. If the buffer
// already contains `maxCount` or more segments, the new segment is
// dropped and an error is returned. It's a bad idea to die on an error
// returned from this function; it's usually safe to just ignore the
// return value.
func (jb *JitterBuffer) Push(seg Segment, maxCount int) (err error) {
	jb.cvar.L.Lock()
	defer jb.cvar.L.Unlock()
	defer jb.cvar.Broadcast()
	jb.hdLim = maxCount

	if len(jb.queue) >= maxCount {
		return errors.New("JitterBuffer overfull")
	}
	// calculate the correct sequence number
	expect8b := jb.expect % 256
	queuepos := uint64(seg.Sequence)

	if queuepos >= expect8b && queuepos-expect8b < 128 {
		// [  0   e   n         0]
		queuepos += jb.expect - expect8b
	} else if queuepos < expect8b && queuepos+256-expect8b < 128 {
		// [   e  0    n           0  ]
		queuepos += 256 + jb.expect - expect8b
	} else {
		// drop packet
		kilog.FineDebug("trtp: JitterBuffer dropped packet (ex=%v, e8=%v, qp=%v)",
			jb.expect, queuepos, queuepos)
		return
	}

	// return if not too late
	if queuepos >= jb.expect {
		// max seqnum
		if queuepos > jb.topseq {
			jb.topseq = queuepos
		}

		defer kilog.FineDebug("trtp: JitterBuffer successfully enqueued packet %v (%v)", queuepos, seg.Sequence)
		jb.queue[queuepos] = seg
	}
	return
}
示例#6
0
func (cc *commonCtx) thrProcessUp() {
	for {
		select {
		case seg := <-cc.upch:
			err := struc.Pack(cc.carrier, &seg)
			if err != nil {
				cc.destroy(err)
				return
			}
			kilog.FineDebug("kiricom: commonCtx successfully transmitted %v", seg)
		case <-cc.deadch:
			return
		}
	}
}
示例#7
0
func (cc *ClientCtx) Dial() (io.ReadWriteCloser, error) {
	sok := newKcPayload(cc.limit, cc.cmn.upch)
	select {
	case <-cc.cmn.deadch:
		return nil, io.ErrClosedPipe
	case <-time.After(time.Second * 30):
		cc.Close()
		return nil, io.ErrNoProgress
	case num := <-cc.idpool:
		sok.connid = num
		kilog.FineDebug("kiricom: assigned number %v", num)

		opener := kcSegment{
			Flag:   kcOPEN,
			ConnID: num,
		}

		cc.tablock.Lock()
		cc.conntab[num] = sok
		cc.tablock.Unlock()

		// on death return the number
		go func() {
			<-sok.deadch
			cc.idpool <- num
		}()

		// on global death, die too
		go func() { // death coupler
			select {
			case <-sok.deadch:
			case <-cc.cmn.deadch:
				sok.destroy("global death")
			}
		}()

		select {
		case <-cc.cmn.deadch:
			return nil, io.ErrClosedPipe
		case <-time.After(time.Second * 30):
			return nil, io.ErrNoProgress
		case cc.cmn.upch <- opener:
			return sok, nil
		}
	}
}
示例#8
0
// HLObfsServerHandshake creates a fully obfuscating tunnel over any network connection.
// Note that you have to close the resulting object properly; otherwise memory leaks
// may result! This is the server version.
func HLObfsServerHandshake(secret []byte, underlying io.ReadWriteCloser) (io.ReadWriteCloser, error) {
	kilog.FineDebug("hlobfs: server handshake called")
	realconn, err := LLObfsServerHandshake(secret, underlying)
	if err != nil {
		return nil, err
	}
	toret := new(hlobfsContext)
	toret.randdist = makeProbDistro()
	toret.stop_ch = make(chan int, 256)
	toret.underlying = realconn
	toret.write_lk = make(chan int, 1)
	toret.write_lk <- 0
	toret.read_buff = nil
	toret.readclosed = false
	go hlobfsBackgroundGbg(toret)
	return toret, nil
}
示例#9
0
// EasyListen listens for incoming kiricom connections.
//
// If privkey is nil, then no KiSS would be done, just obfuscation.
func EasyListen(addr string, privkey natrium.EdDSAPrivate, secret []byte) (toret *EasyListener, err error) {
	tcpadd, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return
	}

	toret = new(EasyListener)

	toret.Addr = tcpadd
	toret.Secret = secret
	toret.core, err = net.ListenTCP("tcp", tcpadd)
	if err != nil {
		return
	}
	toret.deadch = make(chan bool)
	toret.sockch = make(chan io.ReadWriteCloser)

	go func() {
		for {
			lol, err := toret.core.Accept()
			if err != nil {
				return
			}
			lol.(*net.TCPConn).SetNoDelay(true)
			go func() {
				obfs, err := kiss.LLObfsServerHandshake(secret, lol)
				if err != nil {
					kilog.FineDebug("kiricom: EasyListen failed HLObfs: %v", err)
					lol.Close()
					return
				}
				if privkey != nil {
					kss, err := kiss.KiSSNamedHandshake(privkey, nil, obfs)
					if err != nil {
						kilog.FineDebug("kiricom: EasyListen failed KiSS: %v", kss)
						obfs.Close()
						return
					}
					obfs = kss
				}

				state := NewServerCtx(32768, obfs)
				go func() {
					<-toret.deadch
					state.Close()
				}()
				defer state.Close()

				for {
					sok, err := state.Accept()
					if err != nil {
						kilog.Debug("kiricom: EasyListen internal accept error %v", err.Error())
						return
					}
					select {
					case toret.sockch <- sok:
					case <-toret.deadch:
						return
					}
				}
			}()
		}
	}()

	return toret, nil
}
func run_icom_ctx(ctx *icom_ctx, KILL func(), is_server bool, do_junk bool, PAUSELIM int) {
	defer KILL()
	socket_table := make([]chan icom_msg, 65536)
	stable_lock := make(chan bool, 1)
	stable_lock <- true

	prob_dist := MakeProbDistro()
	junk_chan := make(chan bool)

	// Write junk echo packets to mask webpage loading
	if do_junk {
		go func() {
			defer KILL()
			for {
				desired_size := prob_dist.Draw() * 2
				select {
				case <-ctx.killswitch:
					return
				case <-junk_chan:
					select {
					case <-ctx.killswitch:
					case ctx.write_ch <- icom_msg{icom_ignore,
						0, make([]byte, desired_size)}:
					default:
					}
				}
			}
		}()
	}

	// Write packets
	go func() {
		defer KILL()
		for {
			select {
			case <-ctx.killswitch:
				return
			case xaxa := <-ctx.write_ch:
				lel := "data"
				if xaxa.flag == icom_ignore {
					lel = "junk"
				} else if xaxa.flag == icom_open {
					lel = "open"
				} else if xaxa.flag == icom_close {
					lel = "clos"
				} else if xaxa.flag == icom_more {
					lel = "more"
				}

				kilog.FineDebug("[ICOM] -> %v\t%v\t%v:%v", do_junk, xaxa.connid,
					lel, len(xaxa.body))

				buffer := new(bytes.Buffer)
				desired_size := prob_dist.Draw()
				prob_dist.Juggle()
				xaxa.WriteTo(buffer)
				if desired_size > len(xaxa.body) && do_junk {
					excess := desired_size - len(xaxa.body)
					padd := icom_msg{icom_ignore, 0, make([]byte, excess)}
					padd.WriteTo(buffer)
				}
				if xaxa.flag == icom_data && do_junk {
					// Draw a waiting period
					wsecs := rand.ExpFloat64() * 3
					wms := int64(wsecs * 1000)
					// Spin off a goroutine to do this!
					go func() {
						time.Sleep(time.Millisecond * time.Duration(wms))
						select {
						case junk_chan <- true:
						default:
						}
					}()
				}
				_, err := ctx.underlying.Write(buffer.Bytes())
				if err != nil {
					return
				}
			}
		}
	}()

	// Keepalive pakkets
	if do_junk {
		go func() {
			for {
				select {
				case <-ctx.killswitch:
					return
				case <-time.After(time.Second * time.Duration(rand.Int()%5)):
					select {
					case <-ctx.killswitch:
						return
					case ctx.write_ch <- icom_msg{icom_ignore, 0, make([]byte, 0)}:
					}
				}
			}
		}()
	}

	// Client side. Writes stuff.
	if !is_server {
		go func() {
			defer KILL()
			for {
				// Accepts clients
				incoming, err := ctx.our_srv.Accept()
				if err != nil {
					kilog.Debug("** icom_ctx dead @ client accept **")
					return
				}
				// Find a connid
				<-stable_lock
				connid := 0
				startidx := rand.Int() % 65536

				// DEBUG!!!
				//startidx = 0

				for i := 0; i < 65536; i++ {
					if socket_table[(i+startidx)%65536] == nil {
						connid = (i + startidx) % 65536
						break
					}
				}
				ctx.write_ch <- icom_msg{icom_open, connid, make([]byte, 0)}
				xaxa := make(chan icom_msg, PAUSELIM)
				socket_table[connid] = xaxa
				stable_lock <- true
				go func() {
					if !do_junk {
						kilog.Debug("ICOM: Opened connid %d", connid)
					}
					icom_tunnel(ctx, KILL, incoming, connid, xaxa, do_junk, PAUSELIM)
					<-stable_lock
					socket_table[connid] = nil
					stable_lock <- true
					if !do_junk {
						kilog.Debug("ICOM: Closed connid %d", connid)
					}
				}()
			}
		}()
	}

	// Reading link
	for {
		var justread icom_msg
		err := justread.ReadFrom(ctx.underlying)
		if err != nil {
			kilog.Debug("** icom_ctx dead @ body ** due to %s", err.Error())
			return
		}

		xaxa := justread

		lel := "data"
		if xaxa.flag == icom_ignore {
			lel = "junk"
		} else if xaxa.flag == icom_open {
			lel = "open"
		} else if xaxa.flag == icom_close {
			lel = "clos"
		} else if xaxa.flag == icom_more {
			lel = "more"
		}

		kilog.FineDebug("[ICOM] <- %v\t%v\t%v:%v", do_junk, xaxa.connid, lel, len(xaxa.body))

		// Now work with the packet
		if justread.flag == icom_ignore {
			continue
		}
		if justread.flag == icom_open && is_server {
			if !do_junk {
				kilog.Debug("ICOM: Accepted connid %d", justread.connid)
			}
			// Open a connection! The caller of accept will unblock this call.
			conn, err := VSConnect(ctx.our_srv)
			if err != nil {
				return
			}
			xaxa := make(chan icom_msg, PAUSELIM)
			<-stable_lock
			socket_table[justread.connid] = xaxa
			stable_lock <- true
			go func() {
				kilog.Debug("ICOM: Began processing connid %d", justread.connid)
				// Tunnel the connection
				icom_tunnel(ctx, KILL, conn, justread.connid, xaxa, do_junk, PAUSELIM)
				if !do_junk {
					kilog.Debug("ICOM: Closed connid %d", justread.connid)
				}
				<-stable_lock
				socket_table[justread.connid] = nil
				stable_lock <- true
			}()
		} else if justread.flag == icom_data ||
			justread.flag == icom_more ||
			justread.flag == icom_close {
			<-stable_lock
			if socket_table[justread.connid] == nil {
				stable_lock <- true
				continue
			}
			ch := socket_table[justread.connid]
			stable_lock <- true
			// Forward the data to the socket
			select {
			case ch <- justread:
			case <-ctx.killswitch:
				return
			default:
			}
		} else {
			kilog.Debug("** icom_ctx dead ** due to invalid packet")
			return
		}
	}
}