Compare commits
6 Commits
master
...
dpedu/ultr
Author | SHA1 | Date |
---|---|---|
dave | ec618653e6 | |
dave | 56db821e89 | |
dave | 54faf24046 | |
dave | 6e53f75326 | |
dave | 202047b6c2 | |
dave | ac77aad4bc |
|
@ -0,0 +1 @@
|
|||
/swarm
|
26
Dockerfile
26
Dockerfile
|
@ -1,3 +1,25 @@
|
|||
FROM scratch
|
||||
COPY swarm /
|
||||
FROM ubuntu:bionic AS builder
|
||||
RUN set -x && \
|
||||
apt-get update && \
|
||||
apt-get install -y wget libczmq4 libczmq-dev libsodium-dev libsodium23 pkg-config && \
|
||||
wget -qO- https://golang.org/dl/go1.16.4.linux-amd64.tar.gz | tar -C /usr/local -xzf- && \
|
||||
rm -vrf /var/lib/apt/lists/*
|
||||
|
||||
ADD . /tmp/code
|
||||
|
||||
RUN export PATH=$PATH:/usr/local/go/bin && \
|
||||
cd /tmp/code && \
|
||||
go build -o swarm cmd/swarm/main.go
|
||||
|
||||
|
||||
FROM ubuntu:bionic
|
||||
RUN set -x && \
|
||||
apt-get update && \
|
||||
apt-get install -y libczmq4 libsodium23 && \
|
||||
rm -vrf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /tmp/code/swarm /swarm
|
||||
|
||||
USER nobody
|
||||
|
||||
ENTRYPOINT ["/swarm"]
|
||||
|
|
12
Makefile
12
Makefile
|
@ -1,4 +1,6 @@
|
|||
BUILDVARS := CGO_ENABLED=0
|
||||
BUILDVARS :=
|
||||
#CGO_ENABLED=0
|
||||
TAG := latest
|
||||
|
||||
.PHONY: swarm
|
||||
swarm:
|
||||
|
@ -13,5 +15,9 @@ clean:
|
|||
rm -vf swarm
|
||||
|
||||
.PHONY: image
|
||||
image: linux
|
||||
docker build -t dockermirror:5000/dpedu/antiroachswarm .
|
||||
image:
|
||||
docker build -t dockermirror:5000/dpedu/antiroachswarm:$(TAG) .
|
||||
|
||||
.PHONY: push
|
||||
push: image
|
||||
docker push dockermirror:5000/dpedu/antiroachswarm:$(TAG)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,9 @@
|
|||
module git.davepedu.com/dave/antiroachlol
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||
gopkg.in/irc.v3 v3.1.4
|
||||
gopkg.in/zeromq/goczmq.v4 v4.1.0
|
||||
)
|
|
@ -0,0 +1,22 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/irc.v3 v3.1.4 h1:DYGMRFbtseXEh+NadmMUFzMraqyuUj4I3iWYFEzDZPc=
|
||||
gopkg.in/irc.v3 v3.1.4/go.mod h1:shO2gz8+PVeS+4E6GAny88Z0YVVQSxQghdrMVGQsR9s=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/zeromq/goczmq.v4 v4.1.0 h1:CE+FE81mGVs2aSlnbfLuS1oAwdcVywyMM2AC1g33imI=
|
||||
gopkg.in/zeromq/goczmq.v4 v4.1.0/go.mod h1:h4IlfePEYMpFdywGr5gAwKhBBj+hiBl/nF4VoSE4k+0=
|
|
@ -0,0 +1,3 @@
|
|||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
|
@ -0,0 +1,3 @@
|
|||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,22 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
noDeadline = time.Time{}
|
||||
aLongTimeAgo = time.Unix(1, 0)
|
||||
)
|
||||
|
||||
func (d *Dialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) {
|
||||
host, port, err := splitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
|
||||
c.SetDeadline(deadline)
|
||||
defer c.SetDeadline(noDeadline)
|
||||
}
|
||||
if ctx != context.Background() {
|
||||
errCh := make(chan error, 1)
|
||||
done := make(chan struct{})
|
||||
defer func() {
|
||||
close(done)
|
||||
if ctxErr == nil {
|
||||
ctxErr = <-errCh
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.SetDeadline(aLongTimeAgo)
|
||||
errCh <- ctx.Err()
|
||||
case <-done:
|
||||
errCh <- nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
b := make([]byte, 0, 6+len(host)) // the size here is just an estimate
|
||||
b = append(b, Version5)
|
||||
if len(d.AuthMethods) == 0 || d.Authenticate == nil {
|
||||
b = append(b, 1, byte(AuthMethodNotRequired))
|
||||
} else {
|
||||
ams := d.AuthMethods
|
||||
if len(ams) > 255 {
|
||||
return nil, errors.New("too many authentication methods")
|
||||
}
|
||||
b = append(b, byte(len(ams)))
|
||||
for _, am := range ams {
|
||||
b = append(b, byte(am))
|
||||
}
|
||||
}
|
||||
if _, ctxErr = c.Write(b); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
if b[0] != Version5 {
|
||||
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
|
||||
}
|
||||
am := AuthMethod(b[1])
|
||||
if am == AuthMethodNoAcceptableMethods {
|
||||
return nil, errors.New("no acceptable authentication methods")
|
||||
}
|
||||
if d.Authenticate != nil {
|
||||
if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
b = b[:0]
|
||||
b = append(b, Version5, byte(d.cmd), 0)
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
b = append(b, AddrTypeIPv4)
|
||||
b = append(b, ip4...)
|
||||
} else if ip6 := ip.To16(); ip6 != nil {
|
||||
b = append(b, AddrTypeIPv6)
|
||||
b = append(b, ip6...)
|
||||
} else {
|
||||
return nil, errors.New("unknown address type")
|
||||
}
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return nil, errors.New("FQDN too long")
|
||||
}
|
||||
b = append(b, AddrTypeFQDN)
|
||||
b = append(b, byte(len(host)))
|
||||
b = append(b, host...)
|
||||
}
|
||||
b = append(b, byte(port>>8), byte(port))
|
||||
if _, ctxErr = c.Write(b); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
if b[0] != Version5 {
|
||||
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
|
||||
}
|
||||
if cmdErr := Reply(b[1]); cmdErr != StatusSucceeded {
|
||||
return nil, errors.New("unknown error " + cmdErr.String())
|
||||
}
|
||||
if b[2] != 0 {
|
||||
return nil, errors.New("non-zero reserved field")
|
||||
}
|
||||
l := 2
|
||||
var a Addr
|
||||
switch b[3] {
|
||||
case AddrTypeIPv4:
|
||||
l += net.IPv4len
|
||||
a.IP = make(net.IP, net.IPv4len)
|
||||
case AddrTypeIPv6:
|
||||
l += net.IPv6len
|
||||
a.IP = make(net.IP, net.IPv6len)
|
||||
case AddrTypeFQDN:
|
||||
if _, err := io.ReadFull(c, b[:1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l += int(b[0])
|
||||
default:
|
||||
return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3])))
|
||||
}
|
||||
if cap(b) < l {
|
||||
b = make([]byte, l)
|
||||
} else {
|
||||
b = b[:l]
|
||||
}
|
||||
if _, ctxErr = io.ReadFull(c, b); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
if a.IP != nil {
|
||||
copy(a.IP, b)
|
||||
} else {
|
||||
a.Name = string(b[:len(b)-2])
|
||||
}
|
||||
a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1])
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func splitHostPort(address string) (string, int, error) {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
portnum, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
if 1 > portnum || portnum > 0xffff {
|
||||
return "", 0, errors.New("port number out of range " + port)
|
||||
}
|
||||
return host, portnum, nil
|
||||
}
|
|
@ -0,0 +1,317 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package socks provides a SOCKS version 5 client implementation.
|
||||
//
|
||||
// SOCKS protocol version 5 is defined in RFC 1928.
|
||||
// Username/Password authentication for SOCKS version 5 is defined in
|
||||
// RFC 1929.
|
||||
package socks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A Command represents a SOCKS command.
|
||||
type Command int
|
||||
|
||||
func (cmd Command) String() string {
|
||||
switch cmd {
|
||||
case CmdConnect:
|
||||
return "socks connect"
|
||||
case cmdBind:
|
||||
return "socks bind"
|
||||
default:
|
||||
return "socks " + strconv.Itoa(int(cmd))
|
||||
}
|
||||
}
|
||||
|
||||
// An AuthMethod represents a SOCKS authentication method.
|
||||
type AuthMethod int
|
||||
|
||||
// A Reply represents a SOCKS command reply code.
|
||||
type Reply int
|
||||
|
||||
func (code Reply) String() string {
|
||||
switch code {
|
||||
case StatusSucceeded:
|
||||
return "succeeded"
|
||||
case 0x01:
|
||||
return "general SOCKS server failure"
|
||||
case 0x02:
|
||||
return "connection not allowed by ruleset"
|
||||
case 0x03:
|
||||
return "network unreachable"
|
||||
case 0x04:
|
||||
return "host unreachable"
|
||||
case 0x05:
|
||||
return "connection refused"
|
||||
case 0x06:
|
||||
return "TTL expired"
|
||||
case 0x07:
|
||||
return "command not supported"
|
||||
case 0x08:
|
||||
return "address type not supported"
|
||||
default:
|
||||
return "unknown code: " + strconv.Itoa(int(code))
|
||||
}
|
||||
}
|
||||
|
||||
// Wire protocol constants.
|
||||
const (
|
||||
Version5 = 0x05
|
||||
|
||||
AddrTypeIPv4 = 0x01
|
||||
AddrTypeFQDN = 0x03
|
||||
AddrTypeIPv6 = 0x04
|
||||
|
||||
CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
|
||||
cmdBind Command = 0x02 // establishes a passive-open forward proxy connection
|
||||
|
||||
AuthMethodNotRequired AuthMethod = 0x00 // no authentication required
|
||||
AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password
|
||||
AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods
|
||||
|
||||
StatusSucceeded Reply = 0x00
|
||||
)
|
||||
|
||||
// An Addr represents a SOCKS-specific address.
|
||||
// Either Name or IP is used exclusively.
|
||||
type Addr struct {
|
||||
Name string // fully-qualified domain name
|
||||
IP net.IP
|
||||
Port int
|
||||
}
|
||||
|
||||
func (a *Addr) Network() string { return "socks" }
|
||||
|
||||
func (a *Addr) String() string {
|
||||
if a == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
port := strconv.Itoa(a.Port)
|
||||
if a.IP == nil {
|
||||
return net.JoinHostPort(a.Name, port)
|
||||
}
|
||||
return net.JoinHostPort(a.IP.String(), port)
|
||||
}
|
||||
|
||||
// A Conn represents a forward proxy connection.
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
|
||||
boundAddr net.Addr
|
||||
}
|
||||
|
||||
// BoundAddr returns the address assigned by the proxy server for
|
||||
// connecting to the command target address from the proxy server.
|
||||
func (c *Conn) BoundAddr() net.Addr {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.boundAddr
|
||||
}
|
||||
|
||||
// A Dialer holds SOCKS-specific options.
|
||||
type Dialer struct {
|
||||
cmd Command // either CmdConnect or cmdBind
|
||||
proxyNetwork string // network between a proxy server and a client
|
||||
proxyAddress string // proxy server address
|
||||
|
||||
// ProxyDial specifies the optional dial function for
|
||||
// establishing the transport connection.
|
||||
ProxyDial func(context.Context, string, string) (net.Conn, error)
|
||||
|
||||
// AuthMethods specifies the list of request authentication
|
||||
// methods.
|
||||
// If empty, SOCKS client requests only AuthMethodNotRequired.
|
||||
AuthMethods []AuthMethod
|
||||
|
||||
// Authenticate specifies the optional authentication
|
||||
// function. It must be non-nil when AuthMethods is not empty.
|
||||
// It must return an error when the authentication is failed.
|
||||
Authenticate func(context.Context, io.ReadWriter, AuthMethod) error
|
||||
}
|
||||
|
||||
// DialContext connects to the provided address on the provided
|
||||
// network.
|
||||
//
|
||||
// The returned error value may be a net.OpError. When the Op field of
|
||||
// net.OpError contains "socks", the Source field contains a proxy
|
||||
// server address and the Addr field contains a command target
|
||||
// address.
|
||||
//
|
||||
// See func Dial of the net package of standard library for a
|
||||
// description of the network and address parameters.
|
||||
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if ctx == nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||
}
|
||||
var err error
|
||||
var c net.Conn
|
||||
if d.ProxyDial != nil {
|
||||
c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
|
||||
} else {
|
||||
var dd net.Dialer
|
||||
c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
|
||||
}
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
a, err := d.connect(ctx, c, address)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
return &Conn{Conn: c, boundAddr: a}, nil
|
||||
}
|
||||
|
||||
// DialWithConn initiates a connection from SOCKS server to the target
|
||||
// network and address using the connection c that is already
|
||||
// connected to the SOCKS server.
|
||||
//
|
||||
// It returns the connection's local address assigned by the SOCKS
|
||||
// server.
|
||||
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if ctx == nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||
}
|
||||
a, err := d.connect(ctx, c, address)
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Dial connects to the provided address on the provided network.
|
||||
//
|
||||
// Unlike DialContext, it returns a raw transport connection instead
|
||||
// of a forward proxy connection.
|
||||
//
|
||||
// Deprecated: Use DialContext or DialWithConn instead.
|
||||
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
var err error
|
||||
var c net.Conn
|
||||
if d.ProxyDial != nil {
|
||||
c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
|
||||
} else {
|
||||
c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
|
||||
}
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (d *Dialer) validateTarget(network, address string) error {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return errors.New("network not implemented")
|
||||
}
|
||||
switch d.cmd {
|
||||
case CmdConnect, cmdBind:
|
||||
default:
|
||||
return errors.New("command not implemented")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
|
||||
for i, s := range []string{d.proxyAddress, address} {
|
||||
host, port, err := splitHostPort(s)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
a := &Addr{Port: port}
|
||||
a.IP = net.ParseIP(host)
|
||||
if a.IP == nil {
|
||||
a.Name = host
|
||||
}
|
||||
if i == 0 {
|
||||
proxy = a
|
||||
} else {
|
||||
dst = a
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewDialer returns a new Dialer that dials through the provided
|
||||
// proxy server's network and address.
|
||||
func NewDialer(network, address string) *Dialer {
|
||||
return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect}
|
||||
}
|
||||
|
||||
const (
|
||||
authUsernamePasswordVersion = 0x01
|
||||
authStatusSucceeded = 0x00
|
||||
)
|
||||
|
||||
// UsernamePassword are the credentials for the username/password
|
||||
// authentication method.
|
||||
type UsernamePassword struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// Authenticate authenticates a pair of username and password with the
|
||||
// proxy server.
|
||||
func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error {
|
||||
switch auth {
|
||||
case AuthMethodNotRequired:
|
||||
return nil
|
||||
case AuthMethodUsernamePassword:
|
||||
if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 {
|
||||
return errors.New("invalid username/password")
|
||||
}
|
||||
b := []byte{authUsernamePasswordVersion}
|
||||
b = append(b, byte(len(up.Username)))
|
||||
b = append(b, up.Username...)
|
||||
b = append(b, byte(len(up.Password)))
|
||||
b = append(b, up.Password...)
|
||||
// TODO(mikio): handle IO deadlines and cancelation if
|
||||
// necessary
|
||||
if _, err := rw.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.ReadFull(rw, b[:2]); err != nil {
|
||||
return err
|
||||
}
|
||||
if b[0] != authUsernamePasswordVersion {
|
||||
return errors.New("invalid username/password version")
|
||||
}
|
||||
if b[1] != authStatusSucceeded {
|
||||
return errors.New("username/password authentication failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
// A ContextDialer dials using a context.
|
||||
type ContextDialer interface {
|
||||
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// Dial works like DialContext on net.Dialer but using a dialer returned by FromEnvironment.
|
||||
//
|
||||
// The passed ctx is only used for returning the Conn, not the lifetime of the Conn.
|
||||
//
|
||||
// Custom dialers (registered via RegisterDialerType) that do not implement ContextDialer
|
||||
// can leak a goroutine for as long as it takes the underlying Dialer implementation to timeout.
|
||||
//
|
||||
// A Conn returned from a successful Dial after the context has been cancelled will be immediately closed.
|
||||
func Dial(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
d := FromEnvironment()
|
||||
if xd, ok := d.(ContextDialer); ok {
|
||||
return xd.DialContext(ctx, network, address)
|
||||
}
|
||||
return dialContext(ctx, d, network, address)
|
||||
}
|
||||
|
||||
// WARNING: this can leak a goroutine for as long as the underlying Dialer implementation takes to timeout
|
||||
// A Conn returned from a successful Dial after the context has been cancelled will be immediately closed.
|
||||
func dialContext(ctx context.Context, d Dialer, network, address string) (net.Conn, error) {
|
||||
var (
|
||||
conn net.Conn
|
||||
done = make(chan struct{}, 1)
|
||||
err error
|
||||
)
|
||||
go func() {
|
||||
conn, err = d.Dial(network, address)
|
||||
close(done)
|
||||
if conn != nil && ctx.Err() != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
case <-done:
|
||||
}
|
||||
return conn, err
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
type direct struct{}
|
||||
|
||||
// Direct implements Dialer by making network connections directly using net.Dial or net.DialContext.
|
||||
var Direct = direct{}
|
||||
|
||||
var (
|
||||
_ Dialer = Direct
|
||||
_ ContextDialer = Direct
|
||||
)
|
||||
|
||||
// Dial directly invokes net.Dial with the supplied parameters.
|
||||
func (direct) Dial(network, addr string) (net.Conn, error) {
|
||||
return net.Dial(network, addr)
|
||||
}
|
||||
|
||||
// DialContext instantiates a net.Dialer and invokes its DialContext receiver with the supplied parameters.
|
||||
func (direct) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
var d net.Dialer
|
||||
return d.DialContext(ctx, network, addr)
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A PerHost directs connections to a default Dialer unless the host name
|
||||
// requested matches one of a number of exceptions.
|
||||
type PerHost struct {
|
||||
def, bypass Dialer
|
||||
|
||||
bypassNetworks []*net.IPNet
|
||||
bypassIPs []net.IP
|
||||
bypassZones []string
|
||||
bypassHosts []string
|
||||
}
|
||||
|
||||
// NewPerHost returns a PerHost Dialer that directs connections to either
|
||||
// defaultDialer or bypass, depending on whether the connection matches one of
|
||||
// the configured rules.
|
||||
func NewPerHost(defaultDialer, bypass Dialer) *PerHost {
|
||||
return &PerHost{
|
||||
def: defaultDialer,
|
||||
bypass: bypass,
|
||||
}
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network through either
|
||||
// defaultDialer or bypass.
|
||||
func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.dialerForRequest(host).Dial(network, addr)
|
||||
}
|
||||
|
||||
// DialContext connects to the address addr on the given network through either
|
||||
// defaultDialer or bypass.
|
||||
func (p *PerHost) DialContext(ctx context.Context, network, addr string) (c net.Conn, err error) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := p.dialerForRequest(host)
|
||||
if x, ok := d.(ContextDialer); ok {
|
||||
return x.DialContext(ctx, network, addr)
|
||||
}
|
||||
return dialContext(ctx, d, network, addr)
|
||||
}
|
||||
|
||||
func (p *PerHost) dialerForRequest(host string) Dialer {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
for _, net := range p.bypassNetworks {
|
||||
if net.Contains(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassIP := range p.bypassIPs {
|
||||
if bypassIP.Equal(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
for _, zone := range p.bypassZones {
|
||||
if strings.HasSuffix(host, zone) {
|
||||
return p.bypass
|
||||
}
|
||||
if host == zone[1:] {
|
||||
// For a zone ".example.com", we match "example.com"
|
||||
// too.
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassHost := range p.bypassHosts {
|
||||
if bypassHost == host {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
// AddFromString parses a string that contains comma-separated values
|
||||
// specifying hosts that should use the bypass proxy. Each value is either an
|
||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
||||
// (localhost). A best effort is made to parse the string and errors are
|
||||
// ignored.
|
||||
func (p *PerHost) AddFromString(s string) {
|
||||
hosts := strings.Split(s, ",")
|
||||
for _, host := range hosts {
|
||||
host = strings.TrimSpace(host)
|
||||
if len(host) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(host, "/") {
|
||||
// We assume that it's a CIDR address like 127.0.0.0/8
|
||||
if _, net, err := net.ParseCIDR(host); err == nil {
|
||||
p.AddNetwork(net)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
p.AddIP(ip)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(host, "*.") {
|
||||
p.AddZone(host[1:])
|
||||
continue
|
||||
}
|
||||
p.AddHost(host)
|
||||
}
|
||||
}
|
||||
|
||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match an IP.
|
||||
func (p *PerHost) AddIP(ip net.IP) {
|
||||
p.bypassIPs = append(p.bypassIPs, ip)
|
||||
}
|
||||
|
||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match.
|
||||
func (p *PerHost) AddNetwork(net *net.IPNet) {
|
||||
p.bypassNetworks = append(p.bypassNetworks, net)
|
||||
}
|
||||
|
||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
||||
// "example.com" matches "example.com" and all of its subdomains.
|
||||
func (p *PerHost) AddZone(zone string) {
|
||||
if strings.HasSuffix(zone, ".") {
|
||||
zone = zone[:len(zone)-1]
|
||||
}
|
||||
if !strings.HasPrefix(zone, ".") {
|
||||
zone = "." + zone
|
||||
}
|
||||
p.bypassZones = append(p.bypassZones, zone)
|
||||
}
|
||||
|
||||
// AddHost specifies a host name that will use the bypass proxy.
|
||||
func (p *PerHost) AddHost(host string) {
|
||||
if strings.HasSuffix(host, ".") {
|
||||
host = host[:len(host)-1]
|
||||
}
|
||||
p.bypassHosts = append(p.bypassHosts, host)
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package proxy provides support for a variety of protocols to proxy network
|
||||
// data.
|
||||
package proxy // import "golang.org/x/net/proxy"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A Dialer is a means to establish a connection.
|
||||
// Custom dialers should also implement ContextDialer.
|
||||
type Dialer interface {
|
||||
// Dial connects to the given address via the proxy.
|
||||
Dial(network, addr string) (c net.Conn, err error)
|
||||
}
|
||||
|
||||
// Auth contains authentication parameters that specific Dialers may require.
|
||||
type Auth struct {
|
||||
User, Password string
|
||||
}
|
||||
|
||||
// FromEnvironment returns the dialer specified by the proxy-related
|
||||
// variables in the environment and makes underlying connections
|
||||
// directly.
|
||||
func FromEnvironment() Dialer {
|
||||
return FromEnvironmentUsing(Direct)
|
||||
}
|
||||
|
||||
// FromEnvironmentUsing returns the dialer specify by the proxy-related
|
||||
// variables in the environment and makes underlying connections
|
||||
// using the provided forwarding Dialer (for instance, a *net.Dialer
|
||||
// with desired configuration).
|
||||
func FromEnvironmentUsing(forward Dialer) Dialer {
|
||||
allProxy := allProxyEnv.Get()
|
||||
if len(allProxy) == 0 {
|
||||
return forward
|
||||
}
|
||||
|
||||
proxyURL, err := url.Parse(allProxy)
|
||||
if err != nil {
|
||||
return forward
|
||||
}
|
||||
proxy, err := FromURL(proxyURL, forward)
|
||||
if err != nil {
|
||||
return forward
|
||||
}
|
||||
|
||||
noProxy := noProxyEnv.Get()
|
||||
if len(noProxy) == 0 {
|
||||
return proxy
|
||||
}
|
||||
|
||||
perHost := NewPerHost(proxy, forward)
|
||||
perHost.AddFromString(noProxy)
|
||||
return perHost
|
||||
}
|
||||
|
||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
||||
// from a URL with such a scheme.
|
||||
var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error)
|
||||
|
||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
||||
// by FromURL.
|
||||
func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) {
|
||||
if proxySchemes == nil {
|
||||
proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error))
|
||||
}
|
||||
proxySchemes[scheme] = f
|
||||
}
|
||||
|
||||
// FromURL returns a Dialer given a URL specification and an underlying
|
||||
// Dialer for it to make network requests.
|
||||
func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
|
||||
var auth *Auth
|
||||
if u.User != nil {
|
||||
auth = new(Auth)
|
||||
auth.User = u.User.Username()
|
||||
if p, ok := u.User.Password(); ok {
|
||||
auth.Password = p
|
||||
}
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "socks5", "socks5h":
|
||||
addr := u.Hostname()
|
||||
port := u.Port()
|
||||
if port == "" {
|
||||
port = "1080"
|
||||
}
|
||||
return SOCKS5("tcp", net.JoinHostPort(addr, port), auth, forward)
|
||||
}
|
||||
|
||||
// If the scheme doesn't match any of the built-in schemes, see if it
|
||||
// was registered by another package.
|
||||
if proxySchemes != nil {
|
||||
if f, ok := proxySchemes[u.Scheme]; ok {
|
||||
return f(u, forward)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
||||
}
|
||||
|
||||
var (
|
||||
allProxyEnv = &envOnce{
|
||||
names: []string{"ALL_PROXY", "all_proxy"},
|
||||
}
|
||||
noProxyEnv = &envOnce{
|
||||
names: []string{"NO_PROXY", "no_proxy"},
|
||||
}
|
||||
)
|
||||
|
||||
// envOnce looks up an environment variable (optionally by multiple
|
||||
// names) once. It mitigates expensive lookups on some platforms
|
||||
// (e.g. Windows).
|
||||
// (Borrowed from net/http/transport.go)
|
||||
type envOnce struct {
|
||||
names []string
|
||||
once sync.Once
|
||||
val string
|
||||
}
|
||||
|
||||
func (e *envOnce) Get() string {
|
||||
e.once.Do(e.init)
|
||||
return e.val
|
||||
}
|
||||
|
||||
func (e *envOnce) init() {
|
||||
for _, n := range e.names {
|
||||
e.val = os.Getenv(n)
|
||||
if e.val != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset is used by tests
|
||||
func (e *envOnce) reset() {
|
||||
e.once = sync.Once{}
|
||||
e.val = ""
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"golang.org/x/net/internal/socks"
|
||||
)
|
||||
|
||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given
|
||||
// address with an optional username and password.
|
||||
// See RFC 1928 and RFC 1929.
|
||||
func SOCKS5(network, address string, auth *Auth, forward Dialer) (Dialer, error) {
|
||||
d := socks.NewDialer(network, address)
|
||||
if forward != nil {
|
||||
if f, ok := forward.(ContextDialer); ok {
|
||||
d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) {
|
||||
return f.DialContext(ctx, network, address)
|
||||
}
|
||||
} else {
|
||||
d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) {
|
||||
return dialContext(ctx, forward, network, address)
|
||||
}
|
||||
}
|
||||
}
|
||||
if auth != nil {
|
||||
up := socks.UsernamePassword{
|
||||
Username: auth.User,
|
||||
Password: auth.Password,
|
||||
}
|
||||
d.AuthMethods = []socks.AuthMethod{
|
||||
socks.AuthMethodNotRequired,
|
||||
socks.AuthMethodUsernamePassword,
|
||||
}
|
||||
d.Authenticate = up.Authenticate
|
||||
}
|
||||
return d, nil
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
*.cover
|
||||
*.test
|
||||
*.out
|
||||
|
||||
# As this is a library, we ignore go.sum
|
||||
go.sum
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "testcases"]
|
||||
path = testcases
|
||||
url = https://github.com/go-irc/irc-parser-tests/
|
|
@ -0,0 +1,15 @@
|
|||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- gochecknoglobals
|
||||
- gomnd
|
||||
# TODO: re-enable these
|
||||
- errcheck
|
||||
- funlen
|
||||
# TODO: maybe re-enable these
|
||||
- godox
|
||||
- wsl
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
|
@ -0,0 +1,18 @@
|
|||
Copyright 2016 Kaleb Elwert
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,108 @@
|
|||
# go-irc
|
||||
|
||||
[![GoDoc](https://img.shields.io/badge/doc-GoDoc-blue.svg)](https://godoc.org/github.com/go-irc/irc)
|
||||
[![Build Status](https://img.shields.io/github/workflow/status/go-irc/irc/CI.svg)](https://github.com/go-irc/irc/actions)
|
||||
[![Coverage Status](https://img.shields.io/coveralls/go-irc/irc.svg)](https://coveralls.io/github/go-irc/irc?branch=master)
|
||||
|
||||
This package was originally created to only handle message parsing,
|
||||
but has since been expanded to include a small abstraction around a
|
||||
connection and a simple client.
|
||||
|
||||
This library is not designed to hide any of the IRC elements from
|
||||
you. If you just want to build a simple chat bot and don't want to
|
||||
deal with IRC in particular, there are a number of other libraries
|
||||
which provide a more full featured client if that's what you're
|
||||
looking for.
|
||||
|
||||
This library is meant to stay as simple as possible so it can be a
|
||||
building block for other packages.
|
||||
|
||||
This library aims for API compatibility whenever possible. New
|
||||
functions and other additions will most likely not result in a major
|
||||
version increase unless they break the API. This library aims to
|
||||
follow the semver recommendations mentioned on gopkg.in.
|
||||
|
||||
Due to complications in how to support x/net/context vs the built-in context
|
||||
package, only go 1.7+ is officially supported.
|
||||
|
||||
## Import Paths
|
||||
|
||||
All development happens on the `master` branch and when features are
|
||||
considered stable enough, a new release will be tagged.
|
||||
|
||||
* `gopkg.in/irc.v3` should be used to develop against the commits
|
||||
tagged as stable
|
||||
* In previous versions, `github.com/go-irc/irc` used to be able to be
|
||||
used to develop against the master branch but module support in go
|
||||
seems to have broken this.
|
||||
|
||||
## Development
|
||||
|
||||
In order to run the tests, make sure all submodules are up to date. If you are
|
||||
just using this library, these are not needed.
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"gopkg.in/irc.v3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conn, err := net.Dial("tcp", "chat.freenode.net:6667")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
config := irc.ClientConfig{
|
||||
Nick: "i_have_a_nick",
|
||||
Pass: "password",
|
||||
User: "username",
|
||||
Name: "Full Name",
|
||||
Handler: irc.HandlerFunc(func(c *irc.Client, m *irc.Message) {
|
||||
if m.Command == "001" {
|
||||
// 001 is a welcome event, so we join channels there
|
||||
c.Write("JOIN #bot-test-chan")
|
||||
} else if m.Command == "PRIVMSG" && c.FromChannel(m) {
|
||||
// Create a handler on all messages.
|
||||
c.WriteMessage(&irc.Message{
|
||||
Command: "PRIVMSG",
|
||||
Params: []string{
|
||||
m.Params[0],
|
||||
m.Trailing(),
|
||||
},
|
||||
})
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
// Create the client
|
||||
client := irc.NewClient(conn, config)
|
||||
err = client.Run()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Major Version Changes
|
||||
|
||||
### v1
|
||||
|
||||
Initial release
|
||||
|
||||
### v2
|
||||
|
||||
- CTCP messages will no longer be rewritten. The decision was made that this
|
||||
library should pass through all messages without mangling them.
|
||||
- Remove Message.FromChannel as this is not always accurate, while
|
||||
Client.FromChannel should always be accurate.
|
||||
|
||||
### v3
|
||||
|
||||
- Import path changed back to `gopkg.in/irc.v3` without the version suffix.
|
|
@ -0,0 +1,338 @@
|
|||
package irc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ClientConfig is a structure used to configure a Client.
|
||||
type ClientConfig struct {
|
||||
// General connection information.
|
||||
Nick string
|
||||
Pass string
|
||||
User string
|
||||
Name string
|
||||
|
||||
// Connection settings
|
||||
PingFrequency time.Duration
|
||||
PingTimeout time.Duration
|
||||
|
||||
// SendLimit is how frequent messages can be sent. If this is zero,
|
||||
// there will be no limit.
|
||||
SendLimit time.Duration
|
||||
|
||||
// SendBurst is the number of messages which can be sent in a burst.
|
||||
SendBurst int
|
||||
|
||||
// Handler is used for message dispatching.
|
||||
Handler Handler
|
||||
}
|
||||
|
||||
type cap struct {
|
||||
// Requested means that this cap was requested by the user
|
||||
Requested bool
|
||||
|
||||
// Required will be true if this cap is non-optional
|
||||
Required bool
|
||||
|
||||
// Enabled means that this cap was accepted by the server
|
||||
Enabled bool
|
||||
|
||||
// Available means that the server supports this cap
|
||||
Available bool
|
||||
}
|
||||
|
||||
// Client is a wrapper around Conn which is designed to make common operations
|
||||
// much simpler.
|
||||
type Client struct {
|
||||
*Conn
|
||||
rwc io.ReadWriteCloser
|
||||
config ClientConfig
|
||||
|
||||
// Internal state
|
||||
currentNick string
|
||||
limiter chan struct{}
|
||||
incomingPongChan chan string
|
||||
errChan chan error
|
||||
caps map[string]cap
|
||||
remainingCapResponses int
|
||||
connected bool
|
||||
}
|
||||
|
||||
// NewClient creates a client given an io stream and a client config.
|
||||
func NewClient(rwc io.ReadWriteCloser, config ClientConfig) *Client {
|
||||
c := &Client{
|
||||
Conn: NewConn(rwc),
|
||||
rwc: rwc,
|
||||
config: config,
|
||||
errChan: make(chan error, 1),
|
||||
caps: make(map[string]cap),
|
||||
}
|
||||
|
||||
// Replace the writer writeCallback with one of our own
|
||||
c.Conn.Writer.writeCallback = c.writeCallback
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) writeCallback(w *Writer, line string) error {
|
||||
if c.limiter != nil {
|
||||
<-c.limiter
|
||||
}
|
||||
|
||||
_, err := w.writer.Write([]byte(line + "\r\n"))
|
||||
if err != nil {
|
||||
c.sendError(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// maybeStartLimiter will start a ticker which will limit how quickly messages
|
||||
// can be written to the connection if the SendLimit is set in the config.
|
||||
func (c *Client) maybeStartLimiter(wg *sync.WaitGroup, exiting chan struct{}) {
|
||||
if c.config.SendLimit == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
// If SendBurst is 0, this will be unbuffered, so keep that in mind.
|
||||
c.limiter = make(chan struct{}, c.config.SendBurst)
|
||||
limitTick := time.NewTicker(c.config.SendLimit)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
var done bool
|
||||
for !done {
|
||||
select {
|
||||
case <-limitTick.C:
|
||||
select {
|
||||
case c.limiter <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
case <-exiting:
|
||||
done = true
|
||||
}
|
||||
}
|
||||
|
||||
limitTick.Stop()
|
||||
close(c.limiter)
|
||||
c.limiter = nil
|
||||
}()
|
||||
}
|
||||
|
||||
// maybeStartPingLoop will start a goroutine to send out PING messages at the
|
||||
// PingFrequency in the config if the frequency is not 0.
|
||||
func (c *Client) maybeStartPingLoop(wg *sync.WaitGroup, exiting chan struct{}) {
|
||||
if c.config.PingFrequency <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
c.incomingPongChan = make(chan string, 5)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
pingHandlers := make(map[string]chan struct{})
|
||||
ticker := time.NewTicker(c.config.PingFrequency)
|
||||
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// Each time we get a tick, we send off a ping and start a
|
||||
// goroutine to handle the pong.
|
||||
timestamp := time.Now().Unix()
|
||||
pongChan := make(chan struct{}, 1)
|
||||
pingHandlers[fmt.Sprintf("%d", timestamp)] = pongChan
|
||||
wg.Add(1)
|
||||
go c.handlePing(timestamp, pongChan, wg, exiting)
|
||||
case data := <-c.incomingPongChan:
|
||||
// Make sure the pong gets routed to the correct
|
||||
// goroutine.
|
||||
|
||||
c := pingHandlers[data]
|
||||
delete(pingHandlers, data)
|
||||
|
||||
if c != nil {
|
||||
c <- struct{}{}
|
||||
}
|
||||
case <-exiting:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Client) handlePing(timestamp int64, pongChan chan struct{}, wg *sync.WaitGroup, exiting chan struct{}) {
|
||||
defer wg.Done()
|
||||
|
||||
c.Writef("PING :%d", timestamp)
|
||||
|
||||
timer := time.NewTimer(c.config.PingTimeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-timer.C:
|
||||
c.sendError(errors.New("ping timeout"))
|
||||
case <-pongChan:
|
||||
return
|
||||
case <-exiting:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// maybeStartCapHandshake will run a CAP LS and all the relevant CAP REQ
|
||||
// commands if there are any CAPs requested.
|
||||
func (c *Client) maybeStartCapHandshake() {
|
||||
if len(c.caps) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
c.Write("CAP LS")
|
||||
c.remainingCapResponses = 1 // We count the CAP LS response as a normal response
|
||||
for key, cap := range c.caps {
|
||||
if cap.Requested {
|
||||
c.Writef("CAP REQ :%s", key)
|
||||
c.remainingCapResponses++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CapRequest allows you to request IRCv3 capabilities from the server during
|
||||
// the handshake. The behavior is undefined if this is called before the
|
||||
// handshake completes so it is recommended that this be called before Run. If
|
||||
// the CAP is marked as required, the client will exit if that CAP could not be
|
||||
// negotiated during the handshake.
|
||||
func (c *Client) CapRequest(capName string, required bool) {
|
||||
cap := c.caps[capName]
|
||||
cap.Requested = true
|
||||
cap.Required = cap.Required || required
|
||||
c.caps[capName] = cap
|
||||
}
|
||||
|
||||
// CapEnabled allows you to check if a CAP is enabled for this connection. Note
|
||||
// that it will not be populated until after the CAP handshake is done, so it is
|
||||
// recommended to wait to check this until after a message like 001.
|
||||
func (c *Client) CapEnabled(capName string) bool {
|
||||
return c.caps[capName].Enabled
|
||||
}
|
||||
|
||||
// CapAvailable allows you to check if a CAP is available on this server. Note
|
||||
// that it will not be populated until after the CAP handshake is done, so it is
|
||||
// recommended to wait to check this until after a message like 001.
|
||||
func (c *Client) CapAvailable(capName string) bool {
|
||||
return c.caps[capName].Available
|
||||
}
|
||||
|
||||
func (c *Client) sendError(err error) {
|
||||
select {
|
||||
case c.errChan <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) startReadLoop(wg *sync.WaitGroup, exiting chan struct{}) {
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-exiting:
|
||||
return
|
||||
default:
|
||||
m, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
c.sendError(err)
|
||||
break
|
||||
}
|
||||
|
||||
if f, ok := clientFilters[m.Command]; ok {
|
||||
f(c, m)
|
||||
}
|
||||
|
||||
if c.config.Handler != nil {
|
||||
c.config.Handler.Handle(c, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Run starts the main loop for this IRC connection. Note that it may break in
|
||||
// strange and unexpected ways if it is called again before the first connection
|
||||
// exits.
|
||||
func (c *Client) Run() error {
|
||||
return c.RunContext(context.TODO())
|
||||
}
|
||||
|
||||
// RunContext is the same as Run but a context.Context can be passed in for
|
||||
// cancelation.
|
||||
func (c *Client) RunContext(ctx context.Context) error {
|
||||
// exiting is used by the main goroutine here to ensure any sub-goroutines
|
||||
// get closed when exiting.
|
||||
exiting := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
|
||||
c.maybeStartLimiter(&wg, exiting)
|
||||
c.maybeStartPingLoop(&wg, exiting)
|
||||
|
||||
c.currentNick = c.config.Nick
|
||||
|
||||
if c.config.Pass != "" {
|
||||
c.Writef("PASS :%s", c.config.Pass)
|
||||
}
|
||||
|
||||
c.maybeStartCapHandshake()
|
||||
|
||||
// This feels wrong because it results in CAP LS, CAP REQ, NICK, USER, CAP
|
||||
// END, but it works and lets us keep the code a bit simpler.
|
||||
c.Writef("NICK :%s", c.config.Nick)
|
||||
c.Writef("USER %s 0 * :%s", c.config.User, c.config.Name)
|
||||
|
||||
// Now that the handshake is pretty much done, we can start listening for
|
||||
// messages.
|
||||
c.startReadLoop(&wg, exiting)
|
||||
|
||||
// Wait for an error from any goroutine or for the context to time out, then
|
||||
// signal we're exiting and wait for the goroutines to exit.
|
||||
var err error
|
||||
select {
|
||||
case err = <-c.errChan:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
close(exiting)
|
||||
c.rwc.Close()
|
||||
wg.Wait()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CurrentNick returns what the nick of the client is known to be at this point
|
||||
// in time.
|
||||
func (c *Client) CurrentNick() string {
|
||||
return c.currentNick
|
||||
}
|
||||
|
||||
// FromChannel takes a Message representing a PRIVMSG and returns if that
|
||||
// message came from a channel or directly from a user.
|
||||
func (c *Client) FromChannel(m *Message) bool {
|
||||
if len(m.Params) < 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// The first param is the target, so if this doesn't match the current nick,
|
||||
// the message came from a channel.
|
||||
return m.Params[0] != c.currentNick
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package irc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type clientFilter func(*Client, *Message)
|
||||
|
||||
// clientFilters are pre-processing which happens for certain message
|
||||
// types. These were moved from below to keep the complexity of each
|
||||
// component down.
|
||||
var clientFilters = map[string]clientFilter{
|
||||
"001": handle001,
|
||||
"433": handle433,
|
||||
"437": handle437,
|
||||
"PING": handlePing,
|
||||
"PONG": handlePong,
|
||||
"NICK": handleNick,
|
||||
"CAP": handleCap,
|
||||
}
|
||||
|
||||
// From rfc2812 section 5.1 (Command responses)
|
||||
//
|
||||
// 001 RPL_WELCOME
|
||||
// "Welcome to the Internet Relay Network
|
||||
// <nick>!<user>@<host>"
|
||||
func handle001(c *Client, m *Message) {
|
||||
c.currentNick = m.Params[0]
|
||||
c.connected = true
|
||||
}
|
||||
|
||||
// From rfc2812 section 5.2 (Error Replies)
|
||||
//
|
||||
// 433 ERR_NICKNAMEINUSE
|
||||
// "<nick> :Nickname is already in use"
|
||||
//
|
||||
// - Returned when a NICK message is processed that results
|
||||
// in an attempt to change to a currently existing
|
||||
// nickname.
|
||||
func handle433(c *Client, m *Message) {
|
||||
// We only want to try and handle nick collisions during the initial
|
||||
// handshake.
|
||||
if c.connected {
|
||||
return
|
||||
}
|
||||
c.currentNick += "_"
|
||||
c.Writef("NICK :%s", c.currentNick)
|
||||
}
|
||||
|
||||
// From rfc2812 section 5.2 (Error Replies)
|
||||
//
|
||||
// 437 ERR_UNAVAILRESOURCE
|
||||
// "<nick/channel> :Nick/channel is temporarily unavailable"
|
||||
//
|
||||
// - Returned by a server to a user trying to join a channel
|
||||
// currently blocked by the channel delay mechanism.
|
||||
//
|
||||
// - Returned by a server to a user trying to change nickname
|
||||
// when the desired nickname is blocked by the nick delay
|
||||
// mechanism.
|
||||
func handle437(c *Client, m *Message) {
|
||||
// We only want to try and handle nick collisions during the initial
|
||||
// handshake.
|
||||
if c.connected {
|
||||
return
|
||||
}
|
||||
c.currentNick += "_"
|
||||
c.Writef("NICK :%s", c.currentNick)
|
||||
}
|
||||
|
||||
func handlePing(c *Client, m *Message) {
|
||||
reply := m.Copy()
|
||||
reply.Command = "PONG"
|
||||
c.WriteMessage(reply)
|
||||
}
|
||||
|
||||
func handlePong(c *Client, m *Message) {
|
||||
if c.incomingPongChan != nil {
|
||||
select {
|
||||
case c.incomingPongChan <- m.Trailing():
|
||||
default:
|
||||
// Note that this return isn't really needed, but it helps some code
|
||||
// coverage tools actually see this line.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleNick(c *Client, m *Message) {
|
||||
if m.Prefix.Name == c.currentNick && len(m.Params) > 0 {
|
||||
c.currentNick = m.Params[0]
|
||||
}
|
||||
}
|
||||
|
||||
var capFilters = map[string]clientFilter{
|
||||
"LS": handleCapLs,
|
||||
"ACK": handleCapAck,
|
||||
"NAK": handleCapNak,
|
||||
}
|
||||
|
||||
func handleCap(c *Client, m *Message) {
|
||||
if c.remainingCapResponses <= 0 || len(m.Params) <= 2 {
|
||||
return
|
||||
}
|
||||
|
||||
if filter, ok := capFilters[m.Params[1]]; ok {
|
||||
filter(c, m)
|
||||
}
|
||||
|
||||
if c.remainingCapResponses <= 0 {
|
||||
for key, cap := range c.caps {
|
||||
if cap.Required && !cap.Enabled {
|
||||
c.sendError(fmt.Errorf("CAP %s requested but not accepted", key))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.Write("CAP END")
|
||||
}
|
||||
}
|
||||
|
||||
func handleCapLs(c *Client, m *Message) {
|
||||
for _, key := range strings.Split(m.Trailing(), " ") {
|
||||
cap := c.caps[key]
|
||||
cap.Available = true
|
||||
c.caps[key] = cap
|
||||
}
|
||||
c.remainingCapResponses--
|
||||
}
|
||||
|
||||
func handleCapAck(c *Client, m *Message) {
|
||||
for _, key := range strings.Split(m.Trailing(), " ") {
|
||||
cap := c.caps[key]
|
||||
cap.Enabled = true
|
||||
c.caps[key] = cap
|
||||
}
|
||||
c.remainingCapResponses--
|
||||
}
|
||||
|
||||
func handleCapNak(c *Client, m *Message) {
|
||||
// If we got a NAK and this REQ was required, we need to bail
|
||||
// with an error.
|
||||
for _, key := range strings.Split(m.Trailing(), " ") {
|
||||
if c.caps[key].Required {
|
||||
c.sendError(fmt.Errorf("CAP %s requested but was rejected", key))
|
||||
return
|
||||
}
|
||||
}
|
||||
c.remainingCapResponses--
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package irc
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Conn represents a simple IRC client. It embeds an irc.Reader and an
|
||||
// irc.Writer.
|
||||
type Conn struct {
|
||||
*Reader
|
||||
*Writer
|
||||
}
|
||||
|
||||
// NewConn creates a new Conn
|
||||
func NewConn(rw io.ReadWriter) *Conn {
|
||||
return &Conn{
|
||||
NewReader(rw),
|
||||
NewWriter(rw),
|
||||
}
|
||||
}
|
||||
|
||||
// Writer is the outgoing side of a connection.
|
||||
type Writer struct {
|
||||
// DebugCallback is called for each outgoing message. The name of this may
|
||||
// not be stable.
|
||||
DebugCallback func(line string)
|
||||
|
||||
// Internal fields
|
||||
writer io.Writer
|
||||
writeCallback func(w *Writer, line string) error
|
||||
}
|
||||
|
||||
func defaultWriteCallback(w *Writer, line string) error {
|
||||
_, err := w.writer.Write([]byte(line + "\r\n"))
|
||||
return err
|
||||
}
|
||||
|
||||
// NewWriter creates an irc.Writer from an io.Writer.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{nil, w, defaultWriteCallback}
|
||||
}
|
||||
|
||||
// Write is a simple function which will write the given line to the
|
||||
// underlying connection.
|
||||
func (w *Writer) Write(line string) error {
|
||||
if w.DebugCallback != nil {
|
||||
w.DebugCallback(line)
|
||||
}
|
||||
|
||||
return w.writeCallback(w, line)
|
||||
}
|
||||
|
||||
// Writef is a wrapper around the connection's Write method and
|
||||
// fmt.Sprintf. Simply use it to send a message as you would normally
|
||||
// use fmt.Printf.
|
||||
func (w *Writer) Writef(format string, args ...interface{}) error {
|
||||
return w.Write(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// WriteMessage writes the given message to the stream
|
||||
func (w *Writer) WriteMessage(m *Message) error {
|
||||
return w.Write(m.String())
|
||||
}
|
||||
|
||||
// Reader is the incoming side of a connection. The data will be
|
||||
// buffered, so do not re-use the io.Reader used to create the
|
||||
// irc.Reader.
|
||||
type Reader struct {
|
||||
// DebugCallback is called for each incoming message. The name of this may
|
||||
// not be stable.
|
||||
DebugCallback func(string)
|
||||
|
||||
// Internal fields
|
||||
reader *bufio.Reader
|
||||
}
|
||||
|
||||
// NewReader creates an irc.Reader from an io.Reader. Note that once a reader is
|
||||
// passed into this function, you should no longer use it as it is being used
|
||||
// inside a bufio.Reader so you cannot rely on only the amount of data for a
|
||||
// Message being read when you call ReadMessage.
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return &Reader{
|
||||
nil,
|
||||
bufio.NewReader(r),
|
||||
}
|
||||
}
|
||||
|
||||
// ReadMessage returns the next message from the stream or an error.
|
||||
// It ignores empty messages.
|
||||
func (r *Reader) ReadMessage() (msg *Message, err error) {
|
||||
// It's valid for a message to be empty. Clients should ignore these,
|
||||
// so we do to be good citizens.
|
||||
err = ErrZeroLengthMessage
|
||||
for err == ErrZeroLengthMessage {
|
||||
var line string
|
||||
line, err = r.reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.DebugCallback != nil {
|
||||
r.DebugCallback(line)
|
||||
}
|
||||
|
||||
// Parse the message from our line
|
||||
msg, err = ParseMessage(line)
|
||||
}
|
||||
return msg, err
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
module gopkg.in/irc.v3
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.4.0
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
)
|
|
@ -0,0 +1,16 @@
|
|||
package irc
|
||||
|
||||
// Handler is a simple interface meant for dispatching a message from
|
||||
// a Client connection.
|
||||
type Handler interface {
|
||||
Handle(*Client, *Message)
|
||||
}
|
||||
|
||||
// HandlerFunc is a simple wrapper around a function which allows it
|
||||
// to be used as a Handler.
|
||||
type HandlerFunc func(*Client, *Message)
|
||||
|
||||
// Handle calls f(c, m)
|
||||
func (f HandlerFunc) Handle(c *Client, m *Message) {
|
||||
f(c, m)
|
||||
}
|
|
@ -0,0 +1,688 @@
|
|||
//nolint
|
||||
package irc
|
||||
|
||||
const (
|
||||
// RFC1459
|
||||
RPL_TRACELINK = "200"
|
||||
RPL_TRACECONNECTING = "201"
|
||||
RPL_TRACEHANDSHAKE = "202"
|
||||
RPL_TRACEUNKNOWN = "203"
|
||||
RPL_TRACEOPERATOR = "204"
|
||||
RPL_TRACEUSER = "205"
|
||||
RPL_TRACESERVER = "206"
|
||||
RPL_TRACENEWTYPE = "208"
|
||||
RPL_STATSLINKINFO = "211"
|
||||
RPL_STATSCOMMANDS = "212"
|
||||
RPL_STATSCLINE = "213"
|
||||
RPL_STATSNLINE = "214"
|
||||
RPL_STATSILINE = "215"
|
||||
RPL_STATSKLINE = "216"
|
||||
RPL_STATSQLINE = "217"
|
||||
RPL_STATSYLINE = "218"
|
||||
RPL_ENDOFSTATS = "219"
|
||||
RPL_UMODEIS = "221"
|
||||
RPL_STATSLLINE = "241"
|
||||
RPL_STATSUPTIME = "242"
|
||||
RPL_STATSOLINE = "243"
|
||||
RPL_STATSHLINE = "244"
|
||||
RPL_LUSERCLIENT = "251"
|
||||
RPL_LUSEROP = "252"
|
||||
RPL_LUSERUNKNOWN = "253"
|
||||
RPL_LUSERCHANNELS = "254"
|
||||
RPL_LUSERME = "255"
|
||||
RPL_ADMINME = "256"
|
||||
RPL_ADMINLOC1 = "257"
|
||||
RPL_ADMINLOC2 = "258"
|
||||
RPL_ADMINEMAIL = "259"
|
||||
RPL_TRACELOG = "261"
|
||||
RPL_NONE = "300"
|
||||
RPL_AWAY = "301"
|
||||
RPL_USERHOST = "302"
|
||||
RPL_ISON = "303"
|
||||
RPL_UNAWAY = "305"
|
||||
RPL_NOWAWAY = "306"
|
||||
RPL_WHOISUSER = "311"
|
||||
RPL_WHOISSERVER = "312"
|
||||
RPL_WHOISOPERATOR = "313"
|
||||
RPL_WHOWASUSER = "314"
|
||||
RPL_ENDOFWHO = "315"
|
||||
RPL_WHOISIDLE = "317"
|
||||
RPL_ENDOFWHOIS = "318"
|
||||
RPL_WHOISCHANNELS = "319"
|
||||
RPL_LIST = "322"
|
||||
RPL_LISTEND = "323"
|
||||
RPL_CHANNELMODEIS = "324"
|
||||
RPL_NOTOPIC = "331"
|
||||
RPL_TOPIC = "332"
|
||||
RPL_INVITING = "341"
|
||||
RPL_VERSION = "351"
|
||||
RPL_WHOREPLY = "352"
|
||||
RPL_NAMREPLY = "353"
|
||||
RPL_LINKS = "364"
|
||||
RPL_ENDOFLINKS = "365"
|
||||
RPL_ENDOFNAMES = "366"
|
||||
RPL_BANLIST = "367"
|
||||
RPL_ENDOFBANLIST = "368"
|
||||
RPL_ENDOFWHOWAS = "369"
|
||||
RPL_INFO = "371"
|
||||
RPL_MOTD = "372"
|
||||
RPL_ENDOFINFO = "374"
|
||||
RPL_MOTDSTART = "375"
|
||||
RPL_ENDOFMOTD = "376"
|
||||
RPL_YOUREOPER = "381"
|
||||
RPL_REHASHING = "382"
|
||||
RPL_TIME = "391"
|
||||
RPL_USERSSTART = "392"
|
||||
RPL_USERS = "393"
|
||||
RPL_ENDOFUSERS = "394"
|
||||
RPL_NOUSERS = "395"
|
||||
ERR_NOSUCHNICK = "401"
|
||||
ERR_NOSUCHSERVER = "402"
|
||||
ERR_NOSUCHCHANNEL = "403"
|
||||
ERR_CANNOTSENDTOCHAN = "404"
|
||||
ERR_TOOMANYCHANNELS = "405"
|
||||
ERR_WASNOSUCHNICK = "406"
|
||||
ERR_TOOMANYTARGETS = "407"
|
||||
ERR_NOORIGIN = "409"
|
||||
ERR_NORECIPIENT = "411"
|
||||
ERR_NOTEXTTOSEND = "412"
|
||||
ERR_NOTOPLEVEL = "413"
|
||||
ERR_WILDTOPLEVEL = "414"
|
||||
ERR_UNKNOWNCOMMAND = "421"
|
||||
ERR_NOMOTD = "422"
|
||||
ERR_NOADMININFO = "423"
|
||||
ERR_FILEERROR = "424"
|
||||
ERR_NONICKNAMEGIVEN = "431"
|
||||
ERR_ERRONEUSNICKNAME = "432"
|
||||
ERR_NICKNAMEINUSE = "433"
|
||||
ERR_NICKCOLLISION = "436"
|
||||
ERR_USERNOTINCHANNEL = "441"
|
||||
ERR_NOTONCHANNEL = "442"
|
||||
ERR_USERONCHANNEL = "443"
|
||||
ERR_NOLOGIN = "444"
|
||||
ERR_SUMMONDISABLED = "445"
|
||||
ERR_USERSDISABLED = "446"
|
||||
ERR_NOTREGISTERED = "451"
|
||||
ERR_NEEDMOREPARAMS = "461"
|
||||
ERR_ALREADYREGISTERED = "462"
|
||||
ERR_NOPERMFORHOST = "463"
|
||||
ERR_PASSWDMISMATCH = "464"
|
||||
ERR_YOUREBANNEDCREEP = "465"
|
||||
ERR_KEYSET = "467"
|
||||
ERR_CHANNELISFULL = "471"
|
||||
ERR_UNKNOWNMODE = "472"
|
||||
ERR_INVITEONLYCHAN = "473"
|
||||
ERR_BANNEDFROMCHAN = "474"
|
||||
ERR_BADCHANNELKEY = "475"
|
||||
ERR_NOPRIVILEGES = "481"
|
||||
ERR_CHANOPRIVSNEEDED = "482"
|
||||
ERR_CANTKILLSERVER = "483"
|
||||
ERR_NOOPERHOST = "491"
|
||||
ERR_UMODEUNKNOWNFLAG = "501"
|
||||
ERR_USERSDONTMATCH = "502"
|
||||
|
||||
// RFC1459 (Obsolete)
|
||||
RPL_SERVICEINFO = "231"
|
||||
RPL_ENDOFSERVICES = "232"
|
||||
RPL_SERVICE = "233"
|
||||
RPL_WHOISCHANOP = "316"
|
||||
RPL_LISTSTART = "321"
|
||||
RPL_SUMMONING = "342"
|
||||
RPL_KILLDONE = "361"
|
||||
RPL_CLOSING = "362"
|
||||
RPL_CLOSEEND = "363"
|
||||
RPL_INFOSTART = "373"
|
||||
RPL_MYPORTIS = "384"
|
||||
ERR_YOUWILLBEBANNED = "466"
|
||||
ERR_NOSERVICEHOST = "492"
|
||||
|
||||
// RFC2812
|
||||
RPL_WELCOME = "001"
|
||||
RPL_YOURHOST = "002"
|
||||
RPL_CREATED = "003"
|
||||
RPL_MYINFO = "004"
|
||||
RPL_TRACESERVICE = "207"
|
||||
RPL_TRACECLASS = "209"
|
||||
RPL_SERVLIST = "234"
|
||||
RPL_SERVLISTEND = "235"
|
||||
RPL_STATSVLINE = "240"
|
||||
RPL_STATSBLINE = "247"
|
||||
RPL_STATSDLINE = "250"
|
||||
RPL_TRACEEND = "262"
|
||||
RPL_TRYAGAIN = "263"
|
||||
RPL_UNIQOPIS = "325"
|
||||
RPL_INVITELIST = "346"
|
||||
RPL_ENDOFINVITELIST = "347"
|
||||
RPL_EXCEPTLIST = "348"
|
||||
RPL_ENDOFEXCEPTLIST = "349"
|
||||
RPL_YOURESERVICE = "383"
|
||||
ERR_NOSUCHSERVICE = "408"
|
||||
ERR_BADMASK = "415"
|
||||
ERR_UNAVAILRESOURCE = "437"
|
||||
ERR_BADCHANMASK = "476"
|
||||
ERR_NOCHANMODES = "477"
|
||||
ERR_BANLISTFULL = "478"
|
||||
ERR_RESTRICTED = "484"
|
||||
ERR_UNIQOPRIVSNEEDED = "485"
|
||||
|
||||
// RFC2812 (Obsolete)
|
||||
RPL_BOUNCE = "005"
|
||||
RPL_TRACERECONNECT = "210"
|
||||
RPL_STATSPING = "246"
|
||||
|
||||
// IRCv3
|
||||
ERR_INVALIDCAPCMD = "410" // Undernet?
|
||||
RPL_STARTTLS = "670"
|
||||
ERR_STARTTLS = "691"
|
||||
RPL_MONONLINE = "730" // RatBox
|
||||
RPL_MONOFFLINE = "731" // RatBox
|
||||
RPL_MONLIST = "732" // RatBox
|
||||
RPL_ENDOFMONLIST = "733" // RatBox
|
||||
ERR_MONLISTFULL = "734" // RatBox
|
||||
RPL_WHOISKEYVALUE = "760"
|
||||
RPL_KEYVALUE = "761"
|
||||
RPL_METADATAEND = "762"
|
||||
ERR_METADATALIMIT = "764"
|
||||
ERR_TARGETINVALID = "765"
|
||||
ERR_NOMATCHINGKEY = "766"
|
||||
ERR_KEYINVALID = "767"
|
||||
ERR_KEYNOTSET = "768"
|
||||
ERR_KEYNOPERMISSION = "769"
|
||||
RPL_LOGGEDIN = "900" // Charybdis/Atheme, IRCv3
|
||||
RPL_LOGGEDOUT = "901" // Charybdis/Atheme, IRCv3
|
||||
ERR_NICKLOCKED = "902" // Charybdis/Atheme, IRCv3
|
||||
RPL_SASLSUCCESS = "903" // Charybdis/Atheme, IRCv3
|
||||
ERR_SASLFAIL = "904" // Charybdis/Atheme, IRCv3
|
||||
ERR_SASLTOOLONG = "905" // Charybdis/Atheme, IRCv3
|
||||
ERR_SASLABORTED = "906" // Charybdis/Atheme, IRCv3
|
||||
ERR_SASLALREADY = "907" // Charybdis/Atheme, IRCv3
|
||||
RPL_SASLMECHS = "908" // Charybdis/Atheme, IRCv3
|
||||
|
||||
// Other
|
||||
RPL_ISUPPORT = "005"
|
||||
|
||||
// Ignored
|
||||
//
|
||||
// Anything not in an RFC has not been included because
|
||||
// there are way too many conflicts to deal with.
|
||||
/*
|
||||
RPL_MAP = "006" // Unreal
|
||||
RPL_MAPEND = "007" // Unreal
|
||||
RPL_SNOMASK = "008" // ircu
|
||||
RPL_STATMEMTOT = "009" // ircu
|
||||
RPL_BOUNCE = "010"
|
||||
RPL_YOURCOOKIE = "014" // Hybrid?
|
||||
RPL_MAP = "015" // ircu
|
||||
RPL_MAPMORE = "016" // ircu
|
||||
RPL_MAPEND = "017" // ircu
|
||||
RPL_MAPUSERS = "018" // InspIRCd
|
||||
RPL_HELLO = "020" // rusnet-ircd
|
||||
RPL_APASSWARN_SET = "030" // ircu
|
||||
RPL_APASSWARN_SECRET = "031" // ircu
|
||||
RPL_APASSWARN_CLEAR = "032" // ircu
|
||||
RPL_YOURID = "042" // IRCnet
|
||||
RPL_SAVENICK = "043" // IRCnet
|
||||
RPL_ATTEMPTINGJUNC = "050" // aircd
|
||||
RPL_ATTEMPTINGREROUTE = "051" // aircd
|
||||
RPL_REMOTEISUPPORT = "105" // Unreal
|
||||
RPL_STATS = "210" // aircd
|
||||
RPL_STATSHELP = "210" // Unreal
|
||||
RPL_STATSPLINE = "217" // ircu
|
||||
RPL_STATSPLINE = "220" // Hybrid
|
||||
RPL_STATSBLINE = "220" // Bahamut, Unreal
|
||||
RPL_STATSWLINE = "220" // Nefarious
|
||||
RPL_MODLIST = "222"
|
||||
RPL_SQLINE_NICK = "222" // Unreal
|
||||
RPL_STATSBLINE = "222" // Bahamut
|
||||
RPL_STATSJLINE = "222" // ircu
|
||||
RPL_CODEPAGE = "222" // rusnet-ircd
|
||||
RPL_STATSELINE = "223" // Bahamut
|
||||
RPL_STATSGLINE = "223" // Unreal
|
||||
RPL_CHARSET = "223" // rusnet-ircd
|
||||
RPL_STATSFLINE = "224" // Hybrid, Bahamut
|
||||
RPL_STATSTLINE = "224" // Unreal
|
||||
RPL_STATSDLINE = "225" // Hybrid
|
||||
RPL_STATSCLONE = "225" // Bahamut
|
||||
RPL_STATSELINE = "225" // Unreal
|
||||
RPL_STATSCOUNT = "226" // Bahamut
|
||||
RPL_STATSALINE = "226" // Hybrid
|
||||
RPL_STATSNLINE = "226" // Unreal
|
||||
RPL_STATSGLINE = "227" // Bahamut
|
||||
RPL_STATSVLINE = "227" // Unreal
|
||||
RPL_STATSBLINE = "227" // Rizon
|
||||
RPL_STATSQLINE = "228" // ircu
|
||||
RPL_STATSBANVER = "228" // Unreal
|
||||
RPL_STATSSPAMF = "229" // Unreal
|
||||
RPL_STATSEXCEPTTKL = "230" // Unreal
|
||||
RPL_RULES = "232" // Unreal
|
||||
RPL_STATSVERBOSE = "236" // ircu
|
||||
RPL_STATSENGINE = "237" // ircu
|
||||
RPL_STATSFLINE = "238" // ircu
|
||||
RPL_STATSIAUTH = "239" // IRCnet
|
||||
RPL_STATSXLINE = "240" // AustHex
|
||||
RPL_STATSSLINE = "245" // Bahamut, IRCnet, Hybrid
|
||||
RPL_STATSTLINE = "245" // Hybrid?
|
||||
RPL_STATSSERVICE = "246" // Hybrid
|
||||
RPL_STATSTLINE = "246" // ircu
|
||||
RPL_STATSULINE = "246" // Hybrid
|
||||
RPL_STATSXLINE = "247" // Hybrid, PTlink, Unreal
|
||||
RPL_STATSGLINE = "247" // ircu
|
||||
RPL_STATSULINE = "248" // ircu
|
||||
RPL_STATSDEFINE = "248" // IRCnet
|
||||
RPL_STATSULINE = "249"
|
||||
RPL_STATSDEBUG = "249" // Hybrid
|
||||
RPL_STATSCONN = "250" // ircu, Unreal
|
||||
RPL_TRACEPING = "262"
|
||||
RPL_USINGSSL = "264" // rusnet-ircd
|
||||
RPL_LOCALUSERS = "265" // aircd, Hybrid, Bahamut
|
||||
RPL_GLOBALUSERS = "266" // aircd, Hybrid, Bahamut
|
||||
RPL_START_NETSTAT = "267" // aircd
|
||||
RPL_NETSTAT = "268" // aircd
|
||||
RPL_END_NETSTAT = "269" // aircd
|
||||
RPL_PRIVS = "270" // ircu
|
||||
RPL_SILELIST = "271" // ircu
|
||||
RPL_ENDOFSILELIST = "272" // ircu
|
||||
RPL_NOTIFY = "273" // aircd
|
||||
RPL_ENDNOTIFY = "274" // aircd
|
||||
RPL_STATSDELTA = "274" // IRCnet
|
||||
RPL_STATSDLINE = "275" // ircu, Ultimate
|
||||
RPL_USINGSSL = "275" // Bahamut
|
||||
RPL_WHOISCERTFP = "276" // oftc-hybrid
|
||||
RPL_STATSRLINE = "276" // ircu
|
||||
RPL_GLIST = "280" // ircu
|
||||
RPL_ENDOFGLIST = "281" // ircu
|
||||
RPL_ACCEPTLIST = "281"
|
||||
RPL_ENDOFACCEPT = "282"
|
||||
RPL_JUPELIST = "282" // ircu
|
||||
RPL_ALIST = "283"
|
||||
RPL_ENDOFJUPELIST = "283" // ircu
|
||||
RPL_ENDOFALIST = "284"
|
||||
RPL_FEATURE = "284" // ircu
|
||||
RPL_GLIST_HASH = "285"
|
||||
RPL_CHANINFO_HANDLE = "285" // aircd
|
||||
RPL_NEWHOSTIS = "285" // QuakeNet
|
||||
RPL_CHANINFO_USERS = "286" // aircd
|
||||
RPL_CHKHEAD = "286" // QuakeNet
|
||||
RPL_CHANINFO_CHOPS = "287" // aircd
|
||||
RPL_CHANUSER = "287" // QuakeNet
|
||||
RPL_CHANINFO_VOICES = "288" // aircd
|
||||
RPL_PATCHHEAD = "288" // QuakeNet
|
||||
RPL_CHANINFO_AWAY = "289" // aircd
|
||||
RPL_PATCHCON = "289" // QuakeNet
|
||||
RPL_CHANINFO_OPERS = "290" // aircd
|
||||
RPL_HELPHDR = "290" // Unreal
|
||||
RPL_DATASTR = "290" // QuakeNet
|
||||
RPL_CHANINFO_BANNED = "291" // aircd
|
||||
RPL_HELPOP = "291" // Unreal
|
||||
RPL_ENDOFCHECK = "291" // QuakeNet
|
||||
RPL_CHANINFO_BANS = "292" // aircd
|
||||
RPL_HELPTLR = "292" // Unreal
|
||||
ERR_SEARCHNOMATCH = "292" // Nefarious
|
||||
RPL_CHANINFO_INVITE = "293" // aircd
|
||||
RPL_HELPHLP = "293" // Unreal
|
||||
RPL_CHANINFO_INVITES = "294" // aircd
|
||||
RPL_HELPFWD = "294" // Unreal
|
||||
RPL_CHANINFO_KICK = "295" // aircd
|
||||
RPL_HELPIGN = "295" // Unreal
|
||||
RPL_CHANINFO_KICKS = "296" // aircd
|
||||
RPL_END_CHANINFO = "299" // aircd
|
||||
RPL_TEXT = "304" // irc2?
|
||||
RPL_USERIP = "307"
|
||||
RPL_WHOISREGNICK = "307" // Bahamut, Unreal
|
||||
RPL_SUSERHOST = "307" // AustHex
|
||||
RPL_NOTIFYACTION = "308" // aircd
|
||||
RPL_WHOISADMIN = "308" // Bahamut
|
||||
RPL_RULESSTART = "308" // Unreal
|
||||
RPL_NICKTRACE = "309" // aircd
|
||||
RPL_WHOISSADMIN = "309" // Bahamut
|
||||
RPL_ENDOFRULES = "309" // Unreal
|
||||
RPL_WHOISHELPER = "309" // AustHex
|
||||
RPL_WHOISSVCMSG = "310" // Bahamut
|
||||
RPL_WHOISHELPOP = "310" // Unreal
|
||||
RPL_WHOISSERVICE = "310" // AustHex
|
||||
RPL_WHOISPRIVDEAF = "316" // Nefarious
|
||||
RPL_WHOISVIRT = "320" // AustHex
|
||||
RPL_WHOIS_HIDDEN = "320" // Anothernet
|
||||
RPL_WHOISSPECIAL = "320" // Unreal
|
||||
RPL_CHANNELPASSIS = "325"
|
||||
RPL_WHOISWEBIRC = "325" // Nefarious
|
||||
RPL_NOCHANPASS = "326"
|
||||
RPL_CHPASSUNKNOWN = "327"
|
||||
RPL_WHOISHOST = "327" // rusnet-ircd
|
||||
RPL_CHANNEL_URL = "328" // Bahamut, AustHex
|
||||
RPL_CREATIONTIME = "329" // Bahamut
|
||||
RPL_WHOWAS_TIME = "330"
|
||||
RPL_WHOISACCOUNT = "330" // ircu
|
||||
RPL_TOPICWHOTIME = "333" // ircu
|
||||
RPL_LISTUSAGE = "334" // ircu
|
||||
RPL_COMMANDSYNTAX = "334" // Bahamut
|
||||
RPL_LISTSYNTAX = "334" // Unreal
|
||||
RPL_WHOISBOT = "335" // Unreal
|
||||
RPL_WHOISTEXT = "335" // Hybrid
|
||||
RPL_WHOISACCOUNTONLY = "335" // Nefarious
|
||||
RPL_INVITELIST = "336" // Hybrid
|
||||
RPL_WHOISBOT = "336" // Nefarious
|
||||
RPL_ENDOFINVITELIST = "337" // Hybrid
|
||||
RPL_WHOISTEXT = "337" // Hybrid?
|
||||
RPL_CHANPASSOK = "338"
|
||||
RPL_WHOISACTUALLY = "338" // ircu, Bahamut
|
||||
RPL_BADCHANPASS = "339"
|
||||
RPL_WHOISMARKS = "339" // Nefarious
|
||||
RPL_USERIP = "340" // ircu
|
||||
RPL_WHOISKILL = "343" // Nefarious
|
||||
RPL_WHOISCOUNTRY = "344" // InspIRCd 3.0
|
||||
RPL_INVITED = "345" // GameSurge
|
||||
RPL_WHOISGATEWAY = "350" // InspIRCd 3.0
|
||||
RPL_WHOSPCRPL = "354" // ircu
|
||||
RPL_NAMREPLY_ = "355" // QuakeNet
|
||||
RPL_MAP = "357" // AustHex
|
||||
RPL_MAPMORE = "358" // AustHex
|
||||
RPL_MAPEND = "359" // AustHex
|
||||
RPL_KICKEXPIRED = "377" // aircd
|
||||
RPL_BANEXPIRED = "378" // aircd
|
||||
RPL_WHOISHOST = "378" // Unreal
|
||||
RPL_KICKLINKED = "379" // aircd
|
||||
RPL_WHOISMODES = "379" // Unreal
|
||||
RPL_BANLINKED = "380" // aircd
|
||||
RPL_YOURHELPER = "380" // AustHex
|
||||
RPL_NOTOPERANYMORE = "385" // AustHex, Hybrid, Unreal
|
||||
RPL_QLIST = "386" // Unreal
|
||||
RPL_IRCOPS = "386" // Ultimate
|
||||
RPL_IRCOPSHEADER = "386" // Nefarious
|
||||
RPL_ENDOFQLIST = "387" // Unreal
|
||||
RPL_ENDOFIRCOPS = "387" // Ultimate
|
||||
RPL_IRCOPS = "387" // Nefarious
|
||||
RPL_ALIST = "388" // Unreal
|
||||
RPL_ENDOFIRCOPS = "388" // Nefarious
|
||||
RPL_ENDOFALIST = "389" // Unreal
|
||||
RPL_TIME = "391" // ircu
|
||||
RPL_TIME = "391" // bdq-ircd
|
||||
RPL_TIME = "391"
|
||||
RPL_VISIBLEHOST = "396" // Hybrid
|
||||
RPL_CLONES = "399" // InspIRCd 3.0
|
||||
ERR_UNKNOWNERROR = "400"
|
||||
ERR_NOCOLORSONCHAN = "408" // Bahamut
|
||||
ERR_NOCTRLSONCHAN = "408" // Hybrid
|
||||
ERR_TOOMANYMATCHES = "416" // IRCnet
|
||||
ERR_QUERYTOOLONG = "416" // ircu
|
||||
ERR_INPUTTOOLONG = "417" // ircu
|
||||
ERR_LENGTHTRUNCATED = "419" // aircd
|
||||
ERR_AMBIGUOUSCOMMAND = "420" // InspIRCd
|
||||
ERR_NOOPERMOTD = "425" // Unreal
|
||||
ERR_TOOMANYAWAY = "429" // Bahamut
|
||||
ERR_EVENTNICKCHANGE = "430" // AustHex
|
||||
ERR_SERVICENAMEINUSE = "434" // AustHex?
|
||||
ERR_NORULES = "434" // Unreal, Ultimate
|
||||
ERR_SERVICECONFUSED = "435" // Unreal
|
||||
ERR_BANONCHAN = "435" // Bahamut
|
||||
ERR_BANNICKCHANGE = "437" // ircu
|
||||
ERR_NICKTOOFAST = "438" // ircu
|
||||
ERR_DEAD = "438" // IRCnet
|
||||
ERR_TARGETTOOFAST = "439" // ircu
|
||||
ERR_SERVICESDOWN = "440" // Bahamut, Unreal
|
||||
ERR_NONICKCHANGE = "447" // Unreal
|
||||
ERR_FORBIDDENCHANNEL = "448" // Unreal
|
||||
ERR_NOTIMPLEMENTED = "449" // Undernet
|
||||
ERR_IDCOLLISION = "452"
|
||||
ERR_NICKLOST = "453"
|
||||
ERR_HOSTILENAME = "455" // Unreal
|
||||
ERR_ACCEPTFULL = "456"
|
||||
ERR_ACCEPTEXIST = "457"
|
||||
ERR_ACCEPTNOT = "458"
|
||||
ERR_NOHIDING = "459" // Unreal
|
||||
ERR_NOTFORHALFOPS = "460" // Unreal
|
||||
ERR_INVALIDUSERNAME = "468" // ircu
|
||||
ERR_ONLYSERVERSCANCHANGE = "468" // Bahamut, Unreal
|
||||
ERR_NOCODEPAGE = "468" // rusnet-ircd
|
||||
ERR_LINKSET = "469" // Unreal
|
||||
ERR_LINKCHANNEL = "470" // Unreal
|
||||
ERR_KICKEDFROMCHAN = "470" // aircd
|
||||
ERR_7BIT = "470" // rusnet-ircd
|
||||
ERR_NEEDREGGEDNICK = "477" // Bahamut, ircu, Unreal
|
||||
ERR_BADCHANNAME = "479" // Hybrid
|
||||
ERR_LINKFAIL = "479" // Unreal
|
||||
ERR_NOCOLOR = "479" // rusnet-ircd
|
||||
ERR_NOULINE = "480" // AustHex
|
||||
ERR_CANNOTKNOCK = "480" // Unreal
|
||||
ERR_THROTTLE = "480" // Ratbox
|
||||
ERR_NOWALLOP = "480" // rusnet-ircd
|
||||
ERR_ISCHANSERVICE = "484" // Undernet
|
||||
ERR_DESYNC = "484" // Bahamut, Hybrid, PTlink
|
||||
ERR_ATTACKDENY = "484" // Unreal
|
||||
ERR_KILLDENY = "485" // Unreal
|
||||
ERR_CANTKICKADMIN = "485" // PTlink
|
||||
ERR_ISREALSERVICE = "485" // QuakeNet
|
||||
ERR_CHANBANREASON = "485" // Hybrid
|
||||
ERR_NONONREG = "486" // Unreal?
|
||||
ERR_HTMDISABLED = "486" // Unreal
|
||||
ERR_ACCOUNTONLY = "486" // QuakeNet
|
||||
ERR_RLINED = "486" // rusnet-ircd
|
||||
ERR_CHANTOORECENT = "487" // IRCnet
|
||||
ERR_MSGSERVICES = "487" // Bahamut
|
||||
ERR_NOTFORUSERS = "487" // Unreal?
|
||||
ERR_NONONSSL = "487" // ChatIRCd
|
||||
ERR_TSLESSCHAN = "488" // IRCnet
|
||||
ERR_HTMDISABLED = "488" // Unreal?
|
||||
ERR_NOSSL = "488" // Bahamut
|
||||
ERR_SECUREONLYCHAN = "489" // Unreal
|
||||
ERR_VOICENEEDED = "489" // Undernet
|
||||
ERR_ALLMUSTSSL = "490" // InspIRCd
|
||||
ERR_NOSWEAR = "490" // Unreal
|
||||
ERR_NOCTCP = "492" // Hybrid / Unreal?
|
||||
ERR_CANNOTSENDTOUSER = "492" // Charybdis?
|
||||
ERR_NOSHAREDCHAN = "493" // Bahamut
|
||||
ERR_NOFEATURE = "493" // ircu
|
||||
ERR_BADFEATVALUE = "494" // ircu
|
||||
ERR_OWNMODE = "494" // Bahamut, charybdis?
|
||||
ERR_BADLOGTYPE = "495" // ircu
|
||||
ERR_BADLOGSYS = "496" // ircu
|
||||
ERR_BADLOGVALUE = "497" // ircu
|
||||
ERR_ISOPERLCHAN = "498" // ircu
|
||||
ERR_CHANOWNPRIVNEEDED = "499" // Unreal
|
||||
ERR_TOOMANYJOINS = "500" // Unreal?
|
||||
ERR_NOREHASHPARAM = "500" // rusnet-ircd
|
||||
ERR_CANNOTSETMODER = "500" // InspIRCd
|
||||
ERR_UNKNOWNSNOMASK = "501" // InspIRCd
|
||||
ERR_USERNOTONSERV = "504"
|
||||
ERR_SILELISTFULL = "511" // ircu
|
||||
ERR_TOOMANYWATCH = "512" // Bahamut
|
||||
ERR_NOSUCHGLINE = "512" // ircu
|
||||
ERR_BADPING = "513" // ircu
|
||||
ERR_TOOMANYDCC = "514" // Bahamut
|
||||
ERR_NOSUCHJUPE = "514" // irch
|
||||
ERR_BADEXPIRE = "515" // ircu
|
||||
ERR_DONTCHEAT = "516" // ircu
|
||||
ERR_DISABLED = "517" // ircu
|
||||
ERR_NOINVITE = "518" // Unreal
|
||||
ERR_LONGMASK = "518" // ircu
|
||||
ERR_ADMONLY = "519" // Unreal
|
||||
ERR_TOOMANYUSERS = "519" // ircu
|
||||
ERR_OPERONLY = "520" // Unreal
|
||||
ERR_MASKTOOWIDE = "520" // ircu
|
||||
ERR_LISTSYNTAX = "521" // Bahamut
|
||||
ERR_NOSUCHGLINE = "521" // Nefarious
|
||||
ERR_WHOSYNTAX = "522" // Bahamut
|
||||
ERR_WHOLIMEXCEED = "523" // Bahamut
|
||||
ERR_QUARANTINED = "524" // ircu
|
||||
ERR_OPERSPVERIFY = "524" // Unreal
|
||||
ERR_HELPNOTFOUND = "524" // Hybrid
|
||||
ERR_INVALIDKEY = "525" // ircu
|
||||
ERR_CANTSENDTOUSER = "531" // InspIRCd
|
||||
ERR_BADHOSTMASK = "550" // QuakeNet
|
||||
ERR_HOSTUNAVAIL = "551" // QuakeNet
|
||||
ERR_USINGSLINE = "552" // QuakeNet
|
||||
ERR_STATSSLINE = "553" // QuakeNet
|
||||
ERR_NOTLOWEROPLEVEL = "560" // ircu
|
||||
ERR_NOTMANAGER = "561" // ircu
|
||||
ERR_CHANSECURED = "562" // ircu
|
||||
ERR_UPASSSET = "563" // ircu
|
||||
ERR_UPASSNOTSET = "564" // ircu
|
||||
ERR_NOMANAGER = "566" // ircu
|
||||
ERR_UPASS_SAME_APASS = "567" // ircu
|
||||
ERR_LASTERROR = "568" // ircu
|
||||
RPL_NOOMOTD = "568" // Nefarious
|
||||
RPL_REAWAY = "597" // Unreal
|
||||
RPL_GONEAWAY = "598" // Unreal
|
||||
RPL_NOTAWAY = "599" // Unreal
|
||||
RPL_LOGON = "600" // Bahamut, Unreal
|
||||
RPL_LOGOFF = "601" // Bahamut, Unreal
|
||||
RPL_WATCHOFF = "602" // Bahamut, Unreal
|
||||
RPL_WATCHSTAT = "603" // Bahamut, Unreal
|
||||
RPL_NOWON = "604" // Bahamut, Unreal
|
||||
RPL_NOWOFF = "605" // Bahamut, Unreal
|
||||
RPL_WATCHLIST = "606" // Bahamut, Unreal
|
||||
RPL_ENDOFWATCHLIST = "607" // Bahamut, Unreal
|
||||
RPL_WATCHCLEAR = "608" // Ultimate
|
||||
RPL_NOWISAWAY = "609" // Unreal
|
||||
RPL_MAPMORE = "610" // Unreal
|
||||
RPL_ISOPER = "610" // Ultimate
|
||||
RPL_ISLOCOP = "611" // Ultimate
|
||||
RPL_ISNOTOPER = "612" // Ultimate
|
||||
RPL_ENDOFISOPER = "613" // Ultimate
|
||||
RPL_MAPMORE = "615" // PTlink
|
||||
RPL_WHOISMODES = "615" // Ultimate
|
||||
RPL_WHOISHOST = "616" // Ultimate
|
||||
RPL_WHOISSSLFP = "617" // Nefarious
|
||||
RPL_DCCSTATUS = "617" // Bahamut
|
||||
RPL_WHOISBOT = "617" // Ultimate
|
||||
RPL_DCCLIST = "618" // Bahamut
|
||||
RPL_ENDOFDCCLIST = "619" // Bahamut
|
||||
RPL_WHOWASHOST = "619" // Ultimate
|
||||
RPL_DCCINFO = "620" // Bahamut
|
||||
RPL_RULESSTART = "620" // Ultimate
|
||||
RPL_RULES = "621" // Ultimate
|
||||
RPL_ENDOFRULES = "622" // Ultimate
|
||||
RPL_MAPMORE = "623" // Ultimate
|
||||
RPL_OMOTDSTART = "624" // Ultimate
|
||||
RPL_OMOTD = "625" // Ultimate
|
||||
RPL_ENDOFOMOTD = "626" // Ultimate
|
||||
RPL_SETTINGS = "630" // Ultimate
|
||||
RPL_ENDOFSETTINGS = "631" // Ultimate
|
||||
RPL_SYNTAX = "650" // InspIRCd 3.0
|
||||
RPL_CHANNELSMSG = "651" // InspIRCd 3.0
|
||||
RPL_WHOWASIP = "652" // InspIRCd 3.0
|
||||
RPL_UNINVITED = "653" // InspIRCd 3.0
|
||||
RPL_SPAMCMDFWD = "659" // Unreal
|
||||
RPL_WHOISSECURE = "671" // Unreal
|
||||
RPL_UNKNOWNMODES = "672" // Ithildin
|
||||
RPL_WHOISREALIP = "672" // Rizon
|
||||
RPL_CANNOTSETMODES = "673" // Ithildin
|
||||
RPL_WHOISYOURID = "674" // ChatIRCd
|
||||
RPL_LANGUAGES = "690" // Unreal?
|
||||
ERR_INVALIDMODEPARAM = "696" // InspIRCd 3.0
|
||||
ERR_LISTMODEALREADYSET = "697" // InspIRCd 3.0
|
||||
ERR_LISTMODENOTSET = "698" // InspIRCd 3.0
|
||||
RPL_COMMANDS = "700" // InspIRCd 3.0
|
||||
RPL_COMMANDSEND = "701" // InspIRCd 3.0
|
||||
RPL_MODLIST = "702" // RatBox
|
||||
RPL_ENDOFMODLIST = "703" // RatBox
|
||||
RPL_HELPSTART = "704" // RatBox
|
||||
RPL_HELPTXT = "705" // RatBox
|
||||
RPL_ENDOFHELP = "706" // RatBox
|
||||
ERR_TARGCHANGE = "707" // RatBox
|
||||
RPL_ETRACEFULL = "708" // RatBox
|
||||
RPL_ETRACE = "709" // RatBox
|
||||
RPL_KNOCK = "710" // RatBox
|
||||
RPL_KNOCKDLVR = "711" // RatBox
|
||||
ERR_TOOMANYKNOCK = "712" // RatBox
|
||||
ERR_CHANOPEN = "713" // RatBox
|
||||
ERR_KNOCKONCHAN = "714" // RatBox
|
||||
ERR_KNOCKDISABLED = "715" // RatBox
|
||||
ERR_TOOMANYINVITE = "715" // Hybrid
|
||||
RPL_INVITETHROTTLE = "715" // Rizon
|
||||
RPL_TARGUMODEG = "716" // RatBox
|
||||
RPL_TARGNOTIFY = "717" // RatBox
|
||||
RPL_UMODEGMSG = "718" // RatBox
|
||||
RPL_OMOTDSTART = "720" // RatBox
|
||||
RPL_OMOTD = "721" // RatBox
|
||||
RPL_ENDOFOMOTD = "722" // RatBox
|
||||
ERR_NOPRIVS = "723" // RatBox
|
||||
RPL_TESTMASK = "724" // RatBox
|
||||
RPL_TESTLINE = "725" // RatBox
|
||||
RPL_NOTESTLINE = "726" // RatBox
|
||||
RPL_TESTMASKGECOS = "727" // RatBox
|
||||
RPL_QUIETLIST = "728" // Charybdis
|
||||
RPL_ENDOFQUIETLIST = "729" // Charybdis
|
||||
RPL_RSACHALLENGE2 = "740" // RatBox
|
||||
RPL_ENDOFRSACHALLENGE2 = "741" // RatBox
|
||||
ERR_MLOCKRESTRICTED = "742" // Charybdis
|
||||
ERR_INVALIDBAN = "743" // Charybdis
|
||||
ERR_TOPICLOCK = "744" // InspIRCd?
|
||||
RPL_SCANMATCHED = "750" // RatBox
|
||||
RPL_SCANUMODES = "751" // RatBox
|
||||
RPL_ETRACEEND = "759" // irc2.11
|
||||
RPL_XINFO = "771" // Ithildin
|
||||
RPL_XINFOSTART = "773" // Ithildin
|
||||
RPL_XINFOEND = "774" // Ithildin
|
||||
RPL_CHECK = "802" // InspIRCd 3.0
|
||||
RPL_OTHERUMODEIS = "803" // InspIRCd 3.0
|
||||
RPL_OTHERSNOMASKIS = "804" // InspIRCd 3.0
|
||||
ERR_BADCHANNEL = "926" // InspIRCd
|
||||
ERR_INVALIDWATCHNICK = "942" // InspIRCd
|
||||
RPL_IDLETIMESET = "944" // InspIRCd
|
||||
RPL_NICKLOCKOFF = "945" // InspIRCd
|
||||
ERR_NICKNOTLOCKED = "946" // InspIRCd
|
||||
RPL_NICKLOCKON = "947" // InspIRCd
|
||||
ERR_INVALIDIDLETIME = "948" // InspIRCd
|
||||
RPL_UNSILENCED = "950" // InspIRCd
|
||||
RPL_SILENCED = "951" // InspIRCd
|
||||
ERR_NOTSILENCED = "952" // InspIRCd
|
||||
RPL_ENDOFPROPLIST = "960" // InspIRCd
|
||||
RPL_PROPLIST = "961" // InspIRCd
|
||||
ERR_CANNOTDOCOMMAND = "972" // Unreal
|
||||
ERR_CANTUNLOADMODULE = "972" // InspIRCd
|
||||
RPL_UNLOADEDMODULE = "973" // InspIRCd
|
||||
ERR_CANNOTCHANGECHANMODE = "974" // Unreal
|
||||
ERR_CANTLOADMODULE = "974" // InspIRCd
|
||||
RPL_LOADEDMODULE = "975" // InspIRCd
|
||||
ERR_LASTERROR = "975" // Nefarious
|
||||
RPL_SERVLOCKON = "988" // InspIRCd
|
||||
RPL_SERVLOCKOFF = "989" // InspIRCd
|
||||
RPL_DCCALLOWSTART = "990" // InspIRCd
|
||||
RPL_DCCALLOWLIST = "991" // InspIRCd
|
||||
RPL_DCCALLOWEND = "992" // InspIRCd
|
||||
RPL_DCCALLOWTIMED = "993" // InspIRCd
|
||||
RPL_DCCALLOWPERMANENT = "994" // InspIRCd
|
||||
RPL_DCCALLOWREMOVED = "995" // InspIRCd
|
||||
ERR_DCCALLOWINVALID = "996" // InspIRCd
|
||||
RPL_DCCALLOWEXPIRED = "997" // InspIRCd
|
||||
ERR_UNKNOWNDCCALLOWCMD = "998" // InspIRCd
|
||||
ERR_NUMERIC_ERR = "999" // Bahamut
|
||||
//*/
|
||||
|
||||
// Obsolete
|
||||
/*
|
||||
RPL_STATMEM = "010" // ircu
|
||||
RPL_STATSZLINE = "225" // Bahamut
|
||||
RPL_MAPUSERS = "270" // InspIRCd 2.0
|
||||
RPL_VCHANEXIST = "276" // Hybrid
|
||||
RPL_VCHANLIST = "277" // Hybrid
|
||||
RPL_VCHANHELP = "278" // Hybrid 7.0?
|
||||
RPL_CHANNELMLOCKIS = "325" // sorircd
|
||||
RPL_WHOWASREAL = "360" // Charybdis
|
||||
RPL_SPAM = "377" // AustHex
|
||||
RPL_MOTD = "378" // AustHex
|
||||
RPL_WHOWASIP = "379" // InspIRCd 2.0
|
||||
RPL_RSACHALLENGE = "386" // Hybrid
|
||||
ERR_SSLONLYCHAN = "480" // Hybrid
|
||||
ERR_BANNEDNICK = "485" // Ratbox
|
||||
ERR_DELAYREJOIN = "495" // InspIRCd 2.0
|
||||
ERR_GHOSTEDCLIENT = "503" // Hybrid
|
||||
ERR_VWORLDWARN = "503" // AustHex
|
||||
ERR_INVALID_ERROR = "514" // ircu
|
||||
ERR_WHOTRUNC = "520" // AustHex
|
||||
ERR_REMOTEPFX = "525" // CAPAB USERCMDPFX
|
||||
ERR_PFXUNROUTABLE = "526" // CAPAB USERCMDPFX
|
||||
RPL_DUMPING = "640" // Unreal
|
||||
RPL_DUMPRPL = "641" // Unreal
|
||||
RPL_EODUMP = "642" // Unreal
|
||||
RPL_COMMANDS = "702" // InspIRCd 2.0
|
||||
RPL_COMMANDSEND = "703" // InspIRCd 2.0
|
||||
ERR_WORDFILTERED = "936" // InspIRCd
|
||||
ERR_ALREADYCHANFILTERED = "937" // InspIRCd 2.0
|
||||
ERR_NOSUCHCHANFILTER = "938" // InspIRCd 2.0
|
||||
ERR_CHANFILTERFULL = "939" // InspIRCd 2.0
|
||||
RPL_DCCALLOWHELP = "998" // InspIRCd
|
||||
RPL_ENDOFDCCALLOWHELP = "999" // InspIRCd 2.0
|
||||
//*/
|
||||
)
|
|
@ -0,0 +1,403 @@
|
|||
package irc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var tagDecodeSlashMap = map[rune]rune{
|
||||
':': ';',
|
||||
's': ' ',
|
||||
'\\': '\\',
|
||||
'r': '\r',
|
||||
'n': '\n',
|
||||
}
|
||||
|
||||
var tagEncodeMap = map[rune]string{
|
||||
';': "\\:",
|
||||
' ': "\\s",
|
||||
'\\': "\\\\",
|
||||
'\r': "\\r",
|
||||
'\n': "\\n",
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrZeroLengthMessage is returned when parsing if the input is
|
||||
// zero-length.
|
||||
ErrZeroLengthMessage = errors.New("irc: Cannot parse zero-length message")
|
||||
|
||||
// ErrMissingDataAfterPrefix is returned when parsing if there is
|
||||
// no message data after the prefix.
|
||||
ErrMissingDataAfterPrefix = errors.New("irc: No message data after prefix")
|
||||
|
||||
// ErrMissingDataAfterTags is returned when parsing if there is no
|
||||
// message data after the tags.
|
||||
ErrMissingDataAfterTags = errors.New("irc: No message data after tags")
|
||||
|
||||
// ErrMissingCommand is returned when parsing if there is no
|
||||
// command in the parsed message.
|
||||
ErrMissingCommand = errors.New("irc: Missing message command")
|
||||
)
|
||||
|
||||
// TagValue represents the value of a tag.
|
||||
type TagValue string
|
||||
|
||||
// ParseTagValue parses a TagValue from the connection. If you need to
|
||||
// set a TagValue, you probably want to just set the string itself, so
|
||||
// it will be encoded properly.
|
||||
func ParseTagValue(v string) TagValue {
|
||||
ret := &bytes.Buffer{}
|
||||
|
||||
input := bytes.NewBufferString(v)
|
||||
|
||||
for {
|
||||
c, _, err := input.ReadRune()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if c == '\\' {
|
||||
c2, _, err := input.ReadRune()
|
||||
|
||||
// If we got a backslash then the end of the tag value, we should
|
||||
// just ignore the backslash.
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if replacement, ok := tagDecodeSlashMap[c2]; ok {
|
||||
ret.WriteRune(replacement)
|
||||
} else {
|
||||
ret.WriteRune(c2)
|
||||
}
|
||||
} else {
|
||||
ret.WriteRune(c)
|
||||
}
|
||||
}
|
||||
|
||||
return TagValue(ret.String())
|
||||
}
|
||||
|
||||
// Encode converts a TagValue to the format in the connection.
|
||||
func (v TagValue) Encode() string {
|
||||
ret := &bytes.Buffer{}
|
||||
|
||||
for _, c := range v {
|
||||
if replacement, ok := tagEncodeMap[c]; ok {
|
||||
ret.WriteString(replacement)
|
||||
} else {
|
||||
ret.WriteRune(c)
|
||||
}
|
||||
}
|
||||
|
||||
return ret.String()
|
||||
}
|
||||
|
||||
// Tags represents the IRCv3 message tags.
|
||||
type Tags map[string]TagValue
|
||||
|
||||
// ParseTags takes a tag string and parses it into a tag map. It will
|
||||
// always return a tag map, even if there are no valid tags.
|
||||
func ParseTags(line string) Tags {
|
||||
ret := Tags{}
|
||||
|
||||
tags := strings.Split(line, ";")
|
||||
for _, tag := range tags {
|
||||
parts := strings.SplitN(tag, "=", 2)
|
||||
if len(parts) < 2 {
|
||||
ret[parts[0]] = ""
|
||||
continue
|
||||
}
|
||||
|
||||
ret[parts[0]] = ParseTagValue(parts[1])
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetTag is a convenience method to look up a tag in the map.
|
||||
func (t Tags) GetTag(key string) (string, bool) {
|
||||
ret, ok := t[key]
|
||||
return string(ret), ok
|
||||
}
|
||||
|
||||
// Copy will create a new copy of all IRC tags attached to this
|
||||
// message.
|
||||
func (t Tags) Copy() Tags {
|
||||
ret := Tags{}
|
||||
|
||||
for k, v := range t {
|
||||
ret[k] = v
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// String ensures this is stringable
|
||||
func (t Tags) String() string {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
for k, v := range t {
|
||||
buf.WriteByte(';')
|
||||
buf.WriteString(k)
|
||||
if v != "" {
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(v.Encode())
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need the first byte because that's an extra ';'
|
||||
// character.
|
||||
buf.ReadByte()
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Prefix represents the prefix of a message, generally the user who sent it
|
||||
type Prefix struct {
|
||||
// Name will contain the nick of who sent the message, the
|
||||
// server who sent the message, or a blank string
|
||||
Name string
|
||||
|
||||
// User will either contain the user who sent the message or a blank string
|
||||
User string
|
||||
|
||||
// Host will either contain the host of who sent the message or a blank string
|
||||
Host string
|
||||
}
|
||||
|
||||
// ParsePrefix takes an identity string and parses it into an
|
||||
// identity struct. It will always return an Prefix struct and never
|
||||
// nil.
|
||||
func ParsePrefix(line string) *Prefix {
|
||||
// Start by creating an Prefix with nothing but the host
|
||||
id := &Prefix{
|
||||
Name: line,
|
||||
}
|
||||
|
||||
uh := strings.SplitN(id.Name, "@", 2)
|
||||
if len(uh) == 2 {
|
||||
id.Name, id.Host = uh[0], uh[1]
|
||||
}
|
||||
|
||||
nu := strings.SplitN(id.Name, "!", 2)
|
||||
if len(nu) == 2 {
|
||||
id.Name, id.User = nu[0], nu[1]
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
// Copy will create a new copy of an Prefix
|
||||
func (p *Prefix) Copy() *Prefix {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
newPrefix := &Prefix{}
|
||||
|
||||
*newPrefix = *p
|
||||
|
||||
return newPrefix
|
||||
}
|
||||
|
||||
// String ensures this is stringable
|
||||
func (p *Prefix) String() string {
|
||||
buf := &bytes.Buffer{}
|
||||
buf.WriteString(p.Name)
|
||||
|
||||
if p.User != "" {
|
||||
buf.WriteString("!")
|
||||
buf.WriteString(p.User)
|
||||
}
|
||||
|
||||
if p.Host != "" {
|
||||
buf.WriteString("@")
|
||||
buf.WriteString(p.Host)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Message represents a line parsed from the server
|
||||
type Message struct {
|
||||
// Each message can have IRCv3 tags
|
||||
Tags
|
||||
|
||||
// Each message can have a Prefix
|
||||
*Prefix
|
||||
|
||||
// Command is which command is being called.
|
||||
Command string
|
||||
|
||||
// Params are all the arguments for the command.
|
||||
Params []string
|
||||
}
|
||||
|
||||
// MustParseMessage calls ParseMessage and either returns the message
|
||||
// or panics if an error is returned.
|
||||
func MustParseMessage(line string) *Message {
|
||||
m, err := ParseMessage(line)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// ParseMessage takes a message string (usually a whole line) and
|
||||
// parses it into a Message struct. This will return nil in the case
|
||||
// of invalid messages.
|
||||
func ParseMessage(line string) (*Message, error) {
|
||||
// Trim the line and make sure we have data
|
||||
line = strings.TrimRight(line, "\r\n")
|
||||
if len(line) == 0 {
|
||||
return nil, ErrZeroLengthMessage
|
||||
}
|
||||
|
||||
c := &Message{
|
||||
Tags: Tags{},
|
||||
Prefix: &Prefix{},
|
||||
}
|
||||
|
||||
if line[0] == '@' {
|
||||
loc := strings.Index(line, " ")
|
||||
if loc == -1 {
|
||||
return nil, ErrMissingDataAfterTags
|
||||
}
|
||||
|
||||
c.Tags = ParseTags(line[1:loc])
|
||||
line = line[loc+1:]
|
||||
}
|
||||
|
||||
if line[0] == ':' {
|
||||
loc := strings.Index(line, " ")
|
||||
if loc == -1 {
|
||||
return nil, ErrMissingDataAfterPrefix
|
||||
}
|
||||
|
||||
// Parse the identity, if there was one
|
||||
c.Prefix = ParsePrefix(line[1:loc])
|
||||
line = line[loc+1:]
|
||||
}
|
||||
|
||||
// Split out the trailing then the rest of the args. Because
|
||||
// we expect there to be at least one result as an arg (the
|
||||
// command) we don't need to special case the trailing arg and
|
||||
// can just attempt a split on " :"
|
||||
split := strings.SplitN(line, " :", 2)
|
||||
c.Params = strings.FieldsFunc(split[0], func(r rune) bool {
|
||||
return r == ' '
|
||||
})
|
||||
|
||||
// If there are no args, we need to bail because we need at
|
||||
// least the command.
|
||||
if len(c.Params) == 0 {
|
||||
return nil, ErrMissingCommand
|
||||
}
|
||||
|
||||
// If we had a trailing arg, append it to the other args
|
||||
if len(split) == 2 {
|
||||
c.Params = append(c.Params, split[1])
|
||||
}
|
||||
|
||||
// Because of how it's parsed, the Command will show up as the
|
||||
// first arg.
|
||||
c.Command = strings.ToUpper(c.Params[0])
|
||||
c.Params = c.Params[1:]
|
||||
|
||||
// If there are no params, set it to nil, to make writing tests and other
|
||||
// things simpler.
|
||||
if len(c.Params) == 0 {
|
||||
c.Params = nil
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Param returns the i'th argument in the Message or an empty string
|
||||
// if the requested arg does not exist
|
||||
func (m *Message) Param(i int) string {
|
||||
if i < 0 || i >= len(m.Params) {
|
||||
return ""
|
||||
}
|
||||
return m.Params[i]
|
||||
}
|
||||
|
||||
// Trailing returns the last argument in the Message or an empty string
|
||||
// if there are no args
|
||||
func (m *Message) Trailing() string {
|
||||
if len(m.Params) < 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return m.Params[len(m.Params)-1]
|
||||
}
|
||||
|
||||
// Copy will create a new copy of an message
|
||||
func (m *Message) Copy() *Message {
|
||||
// Create a new message
|
||||
newMessage := &Message{}
|
||||
|
||||
// Copy stuff from the old message
|
||||
*newMessage = *m
|
||||
|
||||
// Copy any IRcv3 tags
|
||||
newMessage.Tags = m.Tags.Copy()
|
||||
|
||||
// Copy the Prefix
|
||||
newMessage.Prefix = m.Prefix.Copy()
|
||||
|
||||
// Copy the Params slice
|
||||
newMessage.Params = append(make([]string, 0, len(m.Params)), m.Params...)
|
||||
|
||||
// Similar to parsing, if Params is empty, set it to nil
|
||||
if len(newMessage.Params) == 0 {
|
||||
newMessage.Params = nil
|
||||
}
|
||||
|
||||
return newMessage
|
||||
}
|
||||
|
||||
// String ensures this is stringable
|
||||
func (m *Message) String() string {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
// Write any IRCv3 tags if they exist in the message
|
||||
if len(m.Tags) > 0 {
|
||||
buf.WriteByte('@')
|
||||
buf.WriteString(m.Tags.String())
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
|
||||
// Add the prefix if we have one
|
||||
if m.Prefix != nil && m.Prefix.Name != "" {
|
||||
buf.WriteByte(':')
|
||||
buf.WriteString(m.Prefix.String())
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
|
||||
// Add the command since we know we'll always have one
|
||||
buf.WriteString(m.Command)
|
||||
|
||||
if len(m.Params) > 0 {
|
||||
args := m.Params[:len(m.Params)-1]
|
||||
trailing := m.Params[len(m.Params)-1]
|
||||
|
||||
if len(args) > 0 {
|
||||
buf.WriteByte(' ')
|
||||
buf.WriteString(strings.Join(args, " "))
|
||||
}
|
||||
|
||||
// If trailing is zero-length, contains a space or starts with
|
||||
// a : we need to actually specify that it's trailing.
|
||||
if len(trailing) == 0 || strings.ContainsRune(trailing, ' ') || trailing[0] == ':' {
|
||||
buf.WriteString(" :")
|
||||
} else {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
buf.WriteString(trailing)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package irc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var maskTranslations = map[byte]string{
|
||||
'?': ".",
|
||||
'*': ".*",
|
||||
}
|
||||
|
||||
// MaskToRegex converts an irc mask to a go Regexp for more convenient
|
||||
// use. This should never return an error, but we have this here just
|
||||
// in case.
|
||||
func MaskToRegex(rawMask string) (*regexp.Regexp, error) {
|
||||
input := bytes.NewBufferString(rawMask)
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
output.WriteByte('^')
|
||||
|
||||
for {
|
||||
c, err := input.ReadByte()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if c == '\\' {
|
||||
c, err = input.ReadByte()
|
||||
if err != nil {
|
||||
output.WriteString(regexp.QuoteMeta("\\"))
|
||||
break
|
||||
}
|
||||
|
||||
if c == '?' || c == '*' || c == '\\' {
|
||||
output.WriteString(regexp.QuoteMeta(string(c)))
|
||||
} else {
|
||||
output.WriteString(regexp.QuoteMeta("\\" + string(c)))
|
||||
}
|
||||
} else if trans, ok := maskTranslations[c]; ok {
|
||||
output.WriteString(trans)
|
||||
} else {
|
||||
output.WriteString(regexp.QuoteMeta(string(c)))
|
||||
}
|
||||
}
|
||||
|
||||
output.WriteByte('$')
|
||||
|
||||
return regexp.Compile(output.String())
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
|
@ -0,0 +1,17 @@
|
|||
language: go
|
||||
warnings_are_errors: false
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'deb http://download.opensuse.org/repositories/network:/messaging:/zeromq:/git-draft/xUbuntu_12.04/ ./'
|
||||
key_url: 'http://download.opensuse.org/repositories/network:/messaging:/zeromq:/git-draft/xUbuntu_12.04/Release.key'
|
||||
packages:
|
||||
- libsodium-dev
|
||||
- libczmq-dev
|
||||
|
||||
go:
|
||||
- 1.7.1
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- go test -v .
|
|
@ -0,0 +1,15 @@
|
|||
Contributors (no particular order)
|
||||
==================================
|
||||
|
||||
Brian Knox
|
||||
Luna Duclos
|
||||
Indradhanush Gupta
|
||||
Armen Baghumian
|
||||
Ben Aldrich
|
||||
Jordan Sissel
|
||||
Nick Bargnesi
|
||||
Cyrille Verrier
|
||||
Ben Aldrich
|
||||
Matthew Campbell
|
||||
James Reuss
|
||||
Pieter Hintjens
|
|
@ -0,0 +1,22 @@
|
|||
# Contributing to GoCZMQ
|
||||
|
||||
The contributors are listed in AUTHORS (add yourself). This project uses the MPL v2 license, see LICENSE.
|
||||
|
||||
# Our Process
|
||||
Before you send a pull request, please familiarize yourself with the [C4.1 Collective Code Construction Contract](http://rfc.zeromq.org/spec:22) process. A quick summary (but please, do read the process document):
|
||||
* A Pull Request should be described in the form of a problem statement.
|
||||
* The code included with the pull request should be a proposed solution to that problem.
|
||||
* The submitted code should adhere to our style guidelines (described below).
|
||||
* The submitted code should include tests.
|
||||
* The submitted code should not break any existing tests.
|
||||
|
||||
"A Problem" should be one single clear problem. Large complex problems should be broken down into a series of smaller problems when ever possible.
|
||||
|
||||
**Please be aware** that GoCZMQ is **not versioned**. We merge to master. We deploy from master. Master is epxected to be working, at all times. We strive to do our very best to never break public API in this library. Changes can be additive, but they can not break the existing API. If a case arises where we need to, we will be loud about it on the ZeroMQ mailing list and try to build consensus among current maintainers that it's necessary. We will be very chagrined about it, and you can poke fun at us a bit.
|
||||
|
||||
# Style Guide
|
||||
* Your code must be formatted with [Gofmt](https://blog.golang.org/go-fmt-your-code)
|
||||
* Your code should pass [golint](https://github.com/golang/lint). If for some reason it cannot, please provide an explanation.
|
||||
* Your code should pass [go vet](https://golang.org/cmd/vet/)
|
||||
|
||||
|
|
@ -0,0 +1,362 @@
|
|||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
|
@ -0,0 +1,18 @@
|
|||
build: format clean test
|
||||
go build ./...
|
||||
|
||||
test: get
|
||||
go test -v .
|
||||
|
||||
bench: get
|
||||
go test -v -bench . ./...
|
||||
|
||||
get:
|
||||
go get -t -v ./...
|
||||
|
||||
format:
|
||||
find . -name \*.go -type f -exec gofmt -w {} \;
|
||||
|
||||
clean:
|
||||
|
||||
.PHONY: clean build
|
|
@ -0,0 +1,261 @@
|
|||
# goczmq [![Build Status](https://travis-ci.org/zeromq/goczmq.svg?branch=master)](https://travis-ci.org/zeromq/goczmq) [![Doc Status](https://godoc.org/github.com/zeromq/goczmq?status.png)](https://godoc.org/github.com/zeromq/goczmq)
|
||||
|
||||
## Introduction
|
||||
A golang interface to the [CZMQ v4.2](http://czmq.zeromq.org) API.
|
||||
|
||||
## Install
|
||||
### Dependencies
|
||||
* [libsodium](https://github.com/jedisct1/libsodium)
|
||||
* [libzmq](https://github.com/zeromq/libzmq)
|
||||
* [czmq](https://github.com/zeromq/czmq)
|
||||
|
||||
### For CZMQ master
|
||||
```
|
||||
go get github.com/zeromq/goczmq
|
||||
```
|
||||
#### A Note on Build Tags
|
||||
The CZMQ library includes experimental classes that are not built by default, but can be built
|
||||
by passing `--enable-drafts` to configure. Support for these draft classes are being added
|
||||
to goczmq. To build these features against a CZMQ that has been compiled with `--enable-drafts`,
|
||||
use `go build -tags draft`.
|
||||
|
||||
### For CMZQ = 4.0
|
||||
```
|
||||
go get gopkg.in/zeromq/goczmq.v4
|
||||
```
|
||||
### For CZMQ Before 4.0
|
||||
```
|
||||
go get gopkg.in/zeromq/goczmq.v1
|
||||
```
|
||||
## Usage
|
||||
### Direct CZMQ Sock API
|
||||
#### Example
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/zeromq/goczmq"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a router socket and bind it to port 5555.
|
||||
router, err := goczmq.NewRouter("tcp://*:5555")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer router.Destroy()
|
||||
|
||||
log.Println("router created and bound")
|
||||
|
||||
// Create a dealer socket and connect it to the router.
|
||||
dealer, err := goczmq.NewDealer("tcp://127.0.0.1:5555")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer dealer.Destroy()
|
||||
|
||||
log.Println("dealer created and connected")
|
||||
|
||||
// Send a 'Hello' message from the dealer to the router.
|
||||
// Here we send it as a frame ([]byte), with a FlagNone
|
||||
// flag to indicate there are no more frames following.
|
||||
err = dealer.SendFrame([]byte("Hello"), goczmq.FlagNone)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Println("dealer sent 'Hello'")
|
||||
|
||||
// Receve the message. Here we call RecvMessage, which
|
||||
// will return the message as a slice of frames ([][]byte).
|
||||
// Since this is a router socket that support async
|
||||
// request / reply, the first frame of the message will
|
||||
// be the routing frame.
|
||||
request, err := router.RecvMessage()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("router received '%s' from '%v'", request[1], request[0])
|
||||
|
||||
// Send a reply. First we send the routing frame, which
|
||||
// lets the dealer know which client to send the message.
|
||||
// The FlagMore flag tells the router there will be more
|
||||
// frames in this message.
|
||||
err = router.SendFrame(request[0], goczmq.FlagMore)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("router sent 'World'")
|
||||
|
||||
// Next send the reply. The FlagNone flag tells the router
|
||||
// that this is the last frame of the message.
|
||||
err = router.SendFrame([]byte("World"), goczmq.FlagNone)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Receive the reply.
|
||||
reply, err := dealer.RecvMessage()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("dealer received '%s'", string(reply[0]))
|
||||
}
|
||||
```
|
||||
#### Output
|
||||
```
|
||||
2015/05/26 21:52:52 router created and bound
|
||||
2015/05/26 21:52:52 dealer created and connected
|
||||
2015/05/26 21:52:52 dealer sent 'Hello'
|
||||
2015/05/26 21:52:52 router received 'Hello' from '[0 103 84 189 175]'
|
||||
2015/05/26 21:52:52 router sent 'World'
|
||||
2015/05/26 21:52:52 dealer received 'World'
|
||||
```
|
||||
### io.ReadWriter support
|
||||
#### Example
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/zeromq/goczmq"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a router socket and bind it to port 5555.
|
||||
router, err := goczmq.NewRouter("tcp://*:5555")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer router.Destroy()
|
||||
|
||||
log.Println("router created and bound")
|
||||
|
||||
// Create a dealer socket and connect it to the router.
|
||||
dealer, err := goczmq.NewDealer("tcp://127.0.0.1:5555")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer dealer.Destroy()
|
||||
|
||||
log.Println("dealer created and connected")
|
||||
|
||||
// Send a 'Hello' message from the dealer to the router,
|
||||
// using the io.Write interface
|
||||
n, err := dealer.Write([]byte("Hello"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("dealer sent %d byte message 'Hello'\n", n)
|
||||
|
||||
// Make a byte slice and pass it to the router
|
||||
// Read interface. When using the ReadWriter
|
||||
// interface with a router socket, the router
|
||||
// caches the routing frames internally in a
|
||||
// FIFO and uses them transparently when
|
||||
// sending replies.
|
||||
buf := make([]byte, 16386)
|
||||
|
||||
n, err = router.Read(buf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("router received '%s'\n", buf[:n])
|
||||
|
||||
// Send a reply.
|
||||
n, err = router.Write([]byte("World"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("router sent %d byte message 'World'\n", n)
|
||||
|
||||
// Receive the reply, reusing the previous buffer.
|
||||
n, err = dealer.Read(buf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("dealer received '%s'", string(buf[:n]))
|
||||
}
|
||||
```
|
||||
#### Output
|
||||
```
|
||||
2015/05/26 21:54:10 router created and bound
|
||||
2015/05/26 21:54:10 dealer created and connected
|
||||
2015/05/26 21:54:10 dealer sent 5 byte message 'Hello'
|
||||
2015/05/26 21:54:10 router received 'Hello'
|
||||
2015/05/26 21:54:10 router sent 5 byte message 'World'
|
||||
2015/05/26 21:54:10 dealer received 'World'
|
||||
```
|
||||
### Thread safe channel interface
|
||||
#### Example
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/zeromq/goczmq"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a router channeler and bind it to port 5555.
|
||||
// A channeler provides a thread safe channel interface
|
||||
// to a *Sock
|
||||
router := goczmq.NewRouterChanneler("tcp://*:5555")
|
||||
defer router.Destroy()
|
||||
|
||||
log.Println("router created and bound")
|
||||
|
||||
// Create a dealer channeler and connect it to the router.
|
||||
dealer := goczmq.NewDealerChanneler("tcp://127.0.0.1:5555")
|
||||
defer dealer.Destroy()
|
||||
|
||||
log.Println("dealer created and connected")
|
||||
|
||||
// Send a 'Hello' message from the dealer to the router.
|
||||
dealer.SendChan <- [][]byte{[]byte("Hello")}
|
||||
log.Println("dealer sent 'Hello'")
|
||||
|
||||
// Receve the message as a [][]byte. Since this is
|
||||
// a router, the first frame of the message wil
|
||||
// be the routing frame.
|
||||
request := <-router.RecvChan
|
||||
log.Printf("router received '%s' from '%v'", request[1], request[0])
|
||||
|
||||
// Send a reply. First we send the routing frame, which
|
||||
// lets the dealer know which client to send the message.
|
||||
router.SendChan <- [][]byte{request[0], []byte("World")}
|
||||
log.Printf("router sent 'World'")
|
||||
|
||||
// Receive the reply.
|
||||
reply := <-dealer.RecvChan
|
||||
log.Printf("dealer received '%s'", string(reply[0]))
|
||||
}
|
||||
```
|
||||
#### Output
|
||||
```
|
||||
2015/05/26 21:56:43 router created and bound
|
||||
2015/05/26 21:56:43 dealer created and connected
|
||||
2015/05/26 21:56:43 dealer sent 'Hello'
|
||||
2015/05/26 21:56:43 received 'Hello' from '[0 12 109 153 35]'
|
||||
2015/05/26 21:56:43 router sent 'World'
|
||||
2015/05/26 21:56:43 dealer received 'World'
|
||||
```
|
||||
## GoDoc
|
||||
[godoc](https://godoc.org/github.com/zeromq/goczmq)
|
||||
|
||||
## See Also
|
||||
* [Peter Kleiweg's](https://github.com/pebbe) [zmq4](https://github.com/pebbe/zmq4) bindings
|
||||
|
||||
## License
|
||||
This project uses the MPL v2 license, see LICENSE
|
|
@ -0,0 +1,140 @@
|
|||
package goczmq
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
|
||||
zactor_t *Auth_new () {
|
||||
zactor_t *auth = zactor_new(zauth, NULL); return auth;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Auth wraps the CZMQ zauth actor. It handles authentication
|
||||
// for all incoming connections. It allows whitelisting and
|
||||
// blackisting peers based on IP address and support
|
||||
// PLAIN and CURVE authentication policies.
|
||||
type Auth struct {
|
||||
zactorT *C.struct__zactor_t
|
||||
}
|
||||
|
||||
// NewAuth creates a new Auth actor.
|
||||
func NewAuth() *Auth {
|
||||
z := &Auth{}
|
||||
z.zactorT = C.Auth_new()
|
||||
return z
|
||||
}
|
||||
|
||||
// Verbose sets the auth actor to log information to stdout.
|
||||
func (a *Auth) Verbose() error {
|
||||
cmd := C.CString("VERBOSE")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
rc := C.zstr_send(unsafe.Pointer(a.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
C.zsock_wait(unsafe.Pointer(a.zactorT))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deny adds an address to a socket's deny list
|
||||
func (a *Auth) Deny(address string) error {
|
||||
cmd := C.CString("DENY")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
cAddress := C.CString(address)
|
||||
defer C.free(unsafe.Pointer(cAddress))
|
||||
|
||||
rc := C.zstr_sendm(unsafe.Pointer(a.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_send(unsafe.Pointer(a.zactorT), cAddress)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
C.zsock_wait(unsafe.Pointer(a.zactorT))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Allow removes a previous Deny
|
||||
func (a *Auth) Allow(address string) error {
|
||||
cmd := C.CString("ALLOW")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
cAddress := C.CString(address)
|
||||
defer C.free(unsafe.Pointer(cAddress))
|
||||
|
||||
rc := C.zstr_sendm(unsafe.Pointer(a.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_send(unsafe.Pointer(a.zactorT), cAddress)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
C.zsock_wait(unsafe.Pointer(a.zactorT))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Curve sets auth method to curve
|
||||
func (a *Auth) Curve(allowed string) error {
|
||||
cmd := C.CString("CURVE")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
cAllowed := C.CString(allowed)
|
||||
defer C.free(unsafe.Pointer(cAllowed))
|
||||
|
||||
rc := C.zstr_sendm(unsafe.Pointer(a.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_send(unsafe.Pointer(a.zactorT), cAllowed)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
C.zsock_wait(unsafe.Pointer(a.zactorT))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Plain sets auth method to plain
|
||||
func (a *Auth) Plain(directory string) error {
|
||||
cmd := C.CString("PLAIN")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
cDirectory := C.CString(directory)
|
||||
defer C.free(unsafe.Pointer(cDirectory))
|
||||
|
||||
rc := C.zstr_sendm(unsafe.Pointer(a.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_send(unsafe.Pointer(a.zactorT), cDirectory)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
C.zsock_wait(unsafe.Pointer(a.zactorT))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy destroys the auth actor.
|
||||
func (a *Auth) Destroy() {
|
||||
C.zactor_destroy(&a.zactorT)
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
package goczmq
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
|
||||
zactor_t *Beacon_new () {
|
||||
zactor_t *beacon = zactor_new(zbeacon, NULL); return beacon;
|
||||
}
|
||||
|
||||
int Beacon_publish(void *actor, void *data, int size, int interval) {
|
||||
return zsock_send(actor, "sbi", "PUBLISH", (byte*)data, size, interval);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Beacon wraps the CZMQ beacon actor. It implements a
|
||||
// peer-to-peer discovery service for local networks. Beacons
|
||||
// can broadcast and receive UDPv4 service broadcasts.
|
||||
type Beacon struct {
|
||||
zactorT *C.struct__zactor_t
|
||||
}
|
||||
|
||||
// NewBeacon creates a new Beacon instance.
|
||||
func NewBeacon() *Beacon {
|
||||
z := &Beacon{}
|
||||
z.zactorT = C.Beacon_new()
|
||||
return z
|
||||
}
|
||||
|
||||
// Verbose sets the beacon to log information to stdout.
|
||||
func (b *Beacon) Verbose() error {
|
||||
cmd := C.CString("VERBOSE")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
rc := C.zstr_send(unsafe.Pointer(b.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Configure accepts a port number and configures
|
||||
// the beacon, returning an address
|
||||
func (b *Beacon) Configure(port int) (string, error) {
|
||||
cmd := C.CString("CONFIGURE")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
cPort := C.CString(strconv.Itoa(port))
|
||||
defer C.free(unsafe.Pointer(cPort))
|
||||
|
||||
rc := C.zstr_sendm(unsafe.Pointer(b.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return "", ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_send(unsafe.Pointer(b.zactorT), cPort)
|
||||
if rc == -1 {
|
||||
return "", ErrActorCmd
|
||||
}
|
||||
|
||||
cHostname := C.zstr_recv(unsafe.Pointer(b.zactorT))
|
||||
hostname := C.GoString(cHostname)
|
||||
|
||||
return hostname, nil
|
||||
}
|
||||
|
||||
// Publish publishes an announcement string at an interval
|
||||
func (b *Beacon) Publish(announcement string, interval int) error {
|
||||
cmd := C.CString("PUBLISH")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
cAnnouncement := C.CString(announcement)
|
||||
defer C.free(unsafe.Pointer(cAnnouncement))
|
||||
|
||||
cInterval := C.CString(strconv.Itoa(interval))
|
||||
defer C.free(unsafe.Pointer(cInterval))
|
||||
|
||||
rc := C.zstr_sendm(unsafe.Pointer(b.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_sendm(unsafe.Pointer(b.zactorT), cAnnouncement)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_send(unsafe.Pointer(b.zactorT), cInterval)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PublishBytes publishes an announcement byte slice at an interval
|
||||
func (b *Beacon) PublishBytes(announcement []byte, interval int) error {
|
||||
rc := C.Beacon_publish(
|
||||
unsafe.Pointer(b.zactorT),
|
||||
unsafe.Pointer(&announcement[0]),
|
||||
C.int(len(announcement)),
|
||||
C.int(interval),
|
||||
)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Subscribe subscribes to beacons matching the filter
|
||||
func (b *Beacon) Subscribe(filter string) error {
|
||||
cmd := C.CString("SUBSCRIBE")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
cFilter := C.CString(filter)
|
||||
defer C.free(unsafe.Pointer(cFilter))
|
||||
|
||||
rc := C.zstr_sendm(unsafe.Pointer(b.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_send(unsafe.Pointer(b.zactorT), cFilter)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Recv waits for the specific timeout in milliseconds to receive a beacon
|
||||
func (b *Beacon) Recv(timeout int) [][]byte {
|
||||
C.zsock_set_rcvtimeo(unsafe.Pointer(b.zactorT), C.int(timeout))
|
||||
|
||||
cAddrFrame := C.zframe_recv(unsafe.Pointer(b.zactorT))
|
||||
defer C.zframe_destroy(&cAddrFrame)
|
||||
if cAddrFrame == nil {
|
||||
return nil
|
||||
}
|
||||
addr := C.GoBytes(unsafe.Pointer(C.zframe_data(cAddrFrame)), C.int(C.zframe_size(cAddrFrame)))
|
||||
|
||||
cBeaconFrame := C.zframe_recv(unsafe.Pointer(b.zactorT))
|
||||
defer C.zframe_destroy(&cBeaconFrame)
|
||||
if cBeaconFrame == nil {
|
||||
return nil
|
||||
}
|
||||
beacon := C.GoBytes(unsafe.Pointer(C.zframe_data(cBeaconFrame)), C.int(C.zframe_size(cBeaconFrame)))
|
||||
|
||||
return [][]byte{addr, beacon}
|
||||
}
|
||||
|
||||
// Destroy destroys the beacon.
|
||||
func (b *Beacon) Destroy() {
|
||||
C.zactor_destroy(&b.zactorT)
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package goczmq
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
|
||||
void Set_meta(zcert_t *self, const char *key, const char *value) {zcert_set_meta(self, key, "%s", value);}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Cert wraps the CZMQ zcert class. It provides tools for
|
||||
// creating and working with ZMQ CURVE security certs.
|
||||
// The certs can be used as a temporary object in memory
|
||||
// or persisted to disk. Certs are made up of a public
|
||||
// and secret keypair + metadata.
|
||||
type Cert struct {
|
||||
zcertT *C.struct__zcert_t
|
||||
}
|
||||
|
||||
// NewCert creates a new empty Cert instance
|
||||
func NewCert() *Cert {
|
||||
return &Cert{
|
||||
zcertT: C.zcert_new(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewCertFromKeys creates a new Cert from a public and private key
|
||||
func NewCertFromKeys(public []byte, secret []byte) (*Cert, error) {
|
||||
if len(public) != 32 {
|
||||
return nil, fmt.Errorf("invalid public key")
|
||||
}
|
||||
|
||||
if len(secret) != 32 {
|
||||
return nil, fmt.Errorf("invalid private key")
|
||||
}
|
||||
|
||||
return &Cert{
|
||||
zcertT: C.zcert_new_from(
|
||||
(*C.byte)(unsafe.Pointer(&public[0])),
|
||||
(*C.byte)(unsafe.Pointer(&secret[0]))),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewCertFromFile Load loads a Cert from files
|
||||
func NewCertFromFile(filename string) (*Cert, error) {
|
||||
_, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, ErrCertNotFound
|
||||
}
|
||||
|
||||
cFilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cFilename))
|
||||
|
||||
cert := C.zcert_load(cFilename)
|
||||
return &Cert{
|
||||
zcertT: cert,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetMeta sets meta data for a Cert
|
||||
func (c *Cert) SetMeta(key string, value string) {
|
||||
cKey := C.CString(key)
|
||||
defer C.free(unsafe.Pointer(cKey))
|
||||
|
||||
cValue := C.CString(value)
|
||||
defer C.free(unsafe.Pointer(cValue))
|
||||
|
||||
C.Set_meta(c.zcertT, cKey, cValue)
|
||||
}
|
||||
|
||||
// Meta returns a meta data item from a Cert given a key
|
||||
func (c *Cert) Meta(key string) string {
|
||||
cKey := C.CString(key)
|
||||
defer C.free(unsafe.Pointer(cKey))
|
||||
|
||||
val := C.zcert_meta(c.zcertT, cKey)
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// PublicText returns the public key as a string
|
||||
func (c *Cert) PublicText() string {
|
||||
val := C.zcert_public_txt(c.zcertT)
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// Apply sets the public and private keys for a socket
|
||||
func (c *Cert) Apply(s *Sock) {
|
||||
handle := C.zsock_resolve(unsafe.Pointer(s.zsockT))
|
||||
C.zsock_set_curve_secretkey_bin(handle, C.zcert_secret_key(c.zcertT))
|
||||
C.zsock_set_curve_publickey_bin(handle, C.zcert_public_key(c.zcertT))
|
||||
}
|
||||
|
||||
// Dup duplicates a Cert
|
||||
func (c *Cert) Dup() *Cert {
|
||||
return &Cert{
|
||||
zcertT: C.zcert_dup(c.zcertT),
|
||||
}
|
||||
}
|
||||
|
||||
// Equal checks two Certs for equality
|
||||
func (c *Cert) Equal(compare *Cert) bool {
|
||||
check := C.zcert_eq(c.zcertT, compare.zcertT)
|
||||
if check == C.bool(true) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Print prints a Cert to stdout
|
||||
func (c *Cert) Print() {
|
||||
C.zcert_print(c.zcertT)
|
||||
}
|
||||
|
||||
// SavePublic saves the public key to a file
|
||||
func (c *Cert) SavePublic(filename string) error {
|
||||
cFilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cFilename))
|
||||
|
||||
rc := C.zcert_save_public(c.zcertT, cFilename)
|
||||
if rc == C.int(-1) {
|
||||
return fmt.Errorf("SavePublic error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveSecret saves the secret key to a file
|
||||
func (c *Cert) SaveSecret(filename string) error {
|
||||
cFilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cFilename))
|
||||
|
||||
rc := C.zcert_save_secret(c.zcertT, cFilename)
|
||||
if rc == C.int(-1) {
|
||||
return fmt.Errorf("SaveSecret error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save saves the public and secret key to filename and filename_secret
|
||||
func (c *Cert) Save(filename string) error {
|
||||
cFilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cFilename))
|
||||
|
||||
rc := C.zcert_save(c.zcertT, cFilename)
|
||||
if rc == C.int(-1) {
|
||||
return fmt.Errorf("SavePublic: error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy destroys Cert instance
|
||||
func (c *Cert) Destroy() {
|
||||
C.zcert_destroy(&c.zcertT)
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package goczmq
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
*/
|
||||
import "C"
|
||||
import
|
||||
|
||||
// CertStore works with directories of CURVE security certificates.
|
||||
// It lets you easily load stores from disk and check if a key
|
||||
// is present or not. This could be done fairly easily in pure
|
||||
// Go, but is included for the sake of compatibility.
|
||||
"unsafe"
|
||||
|
||||
type CertStore struct {
|
||||
zcertstoreT *C.struct__zcertstore_t
|
||||
}
|
||||
|
||||
// NewCertStore creates a new certificate store from
|
||||
// a disk directory, loading and indexing all certificates.
|
||||
func NewCertStore(location string) *CertStore {
|
||||
cLocation := C.CString(location)
|
||||
defer C.free(unsafe.Pointer(cLocation))
|
||||
|
||||
return &CertStore{
|
||||
zcertstoreT: C.zcertstore_new(cLocation),
|
||||
}
|
||||
}
|
||||
|
||||
// Insert inserts a certificate into the store in memory.
|
||||
// Call Save directly on the cert if you wish to save it
|
||||
// to disk.
|
||||
func (c *CertStore) Insert(cert *Cert) {
|
||||
C.zcertstore_insert(c.zcertstoreT, &cert.zcertT)
|
||||
}
|
||||
|
||||
// Lookup looks up a certificate in the store by public key and
|
||||
// returns it.
|
||||
func (c *CertStore) Lookup(key string) *Cert {
|
||||
cKey := C.CString(key)
|
||||
defer C.free(unsafe.Pointer(cKey))
|
||||
|
||||
ptr := C.zcertstore_lookup(c.zcertstoreT, cKey)
|
||||
if ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return &Cert{
|
||||
zcertT: ptr,
|
||||
}
|
||||
}
|
||||
|
||||
// Print prints a list of certificates in the store to stdout
|
||||
func (c *CertStore) Print() {
|
||||
C.zcertstore_print(c.zcertstoreT)
|
||||
}
|
||||
|
||||
// Destroy destroys Cert instance
|
||||
func (c *CertStore) Destroy() {
|
||||
C.zcertstore_destroy(&c.zcertstoreT)
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
package goczmq
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
void Sock_init() {zsys_init();}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Channeler serializes all access to a socket through a send
|
||||
// and receive channel. It starts two threads, on is used for receiving
|
||||
// from the zeromq socket. The other is used to listen to the receive
|
||||
// channel, and send everything back to the socket thrad for sending
|
||||
// using an additional inproc socket.
|
||||
type Channeler struct {
|
||||
id int64
|
||||
sockType int
|
||||
endpoints string
|
||||
subscribe *string
|
||||
commandAddr string
|
||||
proxyAddr string
|
||||
commandChan chan<- string
|
||||
SendChan chan<- [][]byte
|
||||
RecvChan <-chan [][]byte
|
||||
}
|
||||
|
||||
// Destroy sends a message to the Channeler to shut it down
|
||||
// and clean it up.
|
||||
func (c *Channeler) Destroy() {
|
||||
c.commandChan <- "destroy"
|
||||
}
|
||||
|
||||
// Subscribe to a Topic
|
||||
func (c *Channeler) Subscribe(topic string) {
|
||||
c.commandChan <- fmt.Sprintf("subscribe %s", topic)
|
||||
}
|
||||
|
||||
// Unsubscribe from a Topic
|
||||
func (c *Channeler) Unsubscribe(topic string) {
|
||||
c.commandChan <- fmt.Sprintf("unsubscribe %s", topic)
|
||||
}
|
||||
|
||||
// actor is a routine that handles communication with
|
||||
// the zeromq socket.
|
||||
func (c *Channeler) actor(recvChan chan<- [][]byte) {
|
||||
pipe, err := NewPair(fmt.Sprintf(">%s", c.commandAddr))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer pipe.Destroy()
|
||||
defer close(recvChan)
|
||||
|
||||
pull, err := NewPull(c.proxyAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer pull.Destroy()
|
||||
|
||||
sock := NewSock(c.sockType)
|
||||
defer sock.Destroy()
|
||||
switch c.sockType {
|
||||
case Pub, Rep, Pull, Router, XPub:
|
||||
err = sock.Attach(c.endpoints, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
case Req, Push, Dealer, Pair, Stream, XSub:
|
||||
err = sock.Attach(c.endpoints, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
case Sub:
|
||||
if c.subscribe != nil {
|
||||
subscriptions := strings.Split(*c.subscribe, ",")
|
||||
for _, topic := range subscriptions {
|
||||
sock.SetSubscribe(topic)
|
||||
}
|
||||
}
|
||||
|
||||
err = sock.Attach(c.endpoints, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
default:
|
||||
panic(ErrInvalidSockType)
|
||||
}
|
||||
|
||||
poller, err := NewPoller(sock, pull, pipe)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer poller.Destroy()
|
||||
|
||||
for {
|
||||
s := poller.Wait(-1)
|
||||
switch s {
|
||||
case pipe:
|
||||
cmd, err := pipe.RecvMessage()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
switch string(cmd[0]) {
|
||||
case "destroy":
|
||||
disconnect := strings.Split(c.endpoints, ",")
|
||||
for _, endpoint := range disconnect {
|
||||
sock.Disconnect(endpoint)
|
||||
}
|
||||
pipe.SendMessage([][]byte{[]byte("ok")})
|
||||
goto ExitActor
|
||||
case "subscribe":
|
||||
topic := string(cmd[1])
|
||||
sock.SetSubscribe(topic)
|
||||
pipe.SendMessage([][]byte{[]byte("ok")})
|
||||
case "unsubscribe":
|
||||
topic := string(cmd[1])
|
||||
sock.SetUnsubscribe(topic)
|
||||
pipe.SendMessage([][]byte{[]byte("ok")})
|
||||
}
|
||||
|
||||
case sock:
|
||||
msg, err := s.RecvMessage()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
recvChan <- msg
|
||||
|
||||
case pull:
|
||||
msg, err := pull.RecvMessage()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = sock.SendMessage(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
ExitActor:
|
||||
}
|
||||
|
||||
// channeler is a routine that handles the channel select loop
|
||||
// and sends commands to the zeromq socket.
|
||||
func (c *Channeler) channeler(commandChan <-chan string, sendChan <-chan [][]byte) {
|
||||
push, err := NewPush(c.proxyAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer push.Destroy()
|
||||
|
||||
pipe, err := NewPair(fmt.Sprintf("@%s", c.commandAddr))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer pipe.Destroy()
|
||||
|
||||
for {
|
||||
select {
|
||||
case cmd := <-commandChan:
|
||||
switch cmd {
|
||||
case "destroy":
|
||||
err = pipe.SendFrame([]byte("destroy"), FlagNone)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = pipe.RecvMessage()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
goto ExitChanneler
|
||||
default:
|
||||
parts := strings.Split(cmd, " ")
|
||||
numParts := len(parts)
|
||||
message := make([][]byte, numParts, numParts)
|
||||
for i, p := range parts {
|
||||
message[i] = []byte(p)
|
||||
}
|
||||
err := pipe.SendMessage(message)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = pipe.RecvMessage()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
case msg := <-sendChan:
|
||||
err := push.SendMessage(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
ExitChanneler:
|
||||
}
|
||||
|
||||
// newChanneler accepts arguments from the socket type based
|
||||
// constructors and creates a new Channeler instance
|
||||
func newChanneler(sockType int, endpoints string, subscribe ...string) *Channeler {
|
||||
commandChan := make(chan string)
|
||||
sendChan := make(chan [][]byte)
|
||||
recvChan := make(chan [][]byte)
|
||||
|
||||
C.Sock_init()
|
||||
c := &Channeler{
|
||||
id: rand.Int63(),
|
||||
endpoints: endpoints,
|
||||
sockType: sockType,
|
||||
commandChan: commandChan,
|
||||
SendChan: sendChan,
|
||||
RecvChan: recvChan,
|
||||
}
|
||||
c.commandAddr = fmt.Sprintf("inproc://actorcontrol%d", c.id)
|
||||
c.proxyAddr = fmt.Sprintf("inproc://proxy%d", c.id)
|
||||
|
||||
if len(subscribe) > 0 {
|
||||
topics := strings.Join(subscribe, ",")
|
||||
c.subscribe = &topics
|
||||
}
|
||||
|
||||
go c.channeler(commandChan, sendChan)
|
||||
go c.actor(recvChan)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// NewPubChanneler creats a new Channeler wrapping
|
||||
// a Pub socket. The socket will bind by default.
|
||||
func NewPubChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(Pub, endpoints, "")
|
||||
}
|
||||
|
||||
// NewSubChanneler creates a new Channeler wrapping
|
||||
// a Sub socket. Along with an endpoint list
|
||||
// it accepts a comma delimited list of topics.
|
||||
// The socket will connect by default.
|
||||
func NewSubChanneler(endpoints string, subscribe ...string) *Channeler {
|
||||
return newChanneler(Sub, endpoints, subscribe...)
|
||||
}
|
||||
|
||||
// NewRepChanneler creates a new Channeler wrapping
|
||||
// a Rep socket. The socket will bind by default.
|
||||
func NewRepChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(Rep, endpoints, "")
|
||||
}
|
||||
|
||||
// NewReqChanneler creates a new Channeler wrapping
|
||||
// a Req socket. The socket will connect by default.
|
||||
func NewReqChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(Req, endpoints, "")
|
||||
}
|
||||
|
||||
// NewPullChanneler creates a new Channeler wrapping
|
||||
// a Pull socket. The socket will bind by default.
|
||||
func NewPullChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(Pull, endpoints, "")
|
||||
}
|
||||
|
||||
// NewPushChanneler creates a new Channeler wrapping
|
||||
// a Push socket. The socket will connect by default.
|
||||
func NewPushChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(Push, endpoints, "")
|
||||
}
|
||||
|
||||
// NewRouterChanneler creates a new Channeler wrapping
|
||||
// a Router socket. The socket will Bind by default.
|
||||
func NewRouterChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(Router, endpoints, "")
|
||||
}
|
||||
|
||||
// NewDealerChanneler creates a new Channeler wrapping
|
||||
// a Dealer socket. The socket will connect by default.
|
||||
func NewDealerChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(Dealer, endpoints, "")
|
||||
}
|
||||
|
||||
// NewXPubChanneler creates a new Channeler wrapping
|
||||
// an XPub socket. The socket will Bind by default.
|
||||
func NewXPubChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(XPub, endpoints, "")
|
||||
}
|
||||
|
||||
// NewXSubChanneler creates a new Channeler wrapping
|
||||
// a XSub socket. The socket will connect by default.
|
||||
func NewXSubChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(XSub, endpoints, "")
|
||||
}
|
||||
|
||||
// NewPairChanneler creates a new Channeler wrapping
|
||||
// a Pair socket. The socket will connect by default.
|
||||
func NewPairChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(Pair, endpoints, "")
|
||||
}
|
||||
|
||||
// NewStreamChanneler creates a new Channeler wrapping
|
||||
// a Pair socket. The socket will connect by default.
|
||||
func NewStreamChanneler(endpoints string) *Channeler {
|
||||
return newChanneler(Stream, endpoints, "")
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
// Package goczmq is a golang binding for CZMQ 3. CZMQ is a high level binding
|
||||
// for ZeroMQ. Along with ZeroMQ socket support, CZMQ provides "actor" based
|
||||
// services for authentication, service discovery, and creating proxies.
|
||||
// GoCZMQ provides direct bindings to CZMQ along with higher level go
|
||||
// abstractions such as channels and io.ReadWriter interface support.
|
||||
//
|
||||
// "Tell them I was a writer.
|
||||
// A maker of software.
|
||||
// A humanist. A father.
|
||||
// And many things.
|
||||
// But above all, a writer.
|
||||
// Thank You. :)
|
||||
// - Pieter Hintjens
|
||||
package goczmq
|
||||
|
||||
/*
|
||||
#cgo !windows pkg-config: libczmq libzmq libsodium
|
||||
#cgo windows LDFLAGS: -lws2_32 -liphlpapi -lrpcrt4 -lsodium -lzmq -lczmq
|
||||
#cgo windows CFLAGS: -Wno-pedantic-ms-format -DLIBCZMQ_EXPORTS -DZMQ_DEFINED_STDINT -DLIBCZMQ_EXPORTS -DZMQ_BUILD_DRAFT_API
|
||||
|
||||
#include "czmq.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// Req is a ZMQ_REQ socket type
|
||||
Req = int(C.ZMQ_REQ)
|
||||
|
||||
// Rep is a ZMQ_REP socket type
|
||||
Rep = int(C.ZMQ_REP)
|
||||
|
||||
// Dealer is a ZMQ_DEALER socket type
|
||||
Dealer = int(C.ZMQ_DEALER)
|
||||
|
||||
// Router is a ZMQ_ROUTER socket type
|
||||
Router = int(C.ZMQ_ROUTER)
|
||||
|
||||
// Pub is a ZMQ_PUB socket type
|
||||
Pub = int(C.ZMQ_PUB)
|
||||
|
||||
// Sub is a ZMQ_SUB socket type
|
||||
Sub = int(C.ZMQ_SUB)
|
||||
|
||||
// XPub is a ZMQ_XPUB socket type
|
||||
XPub = int(C.ZMQ_XPUB)
|
||||
|
||||
// XSub is a ZMQ_XSUB socket type
|
||||
XSub = int(C.ZMQ_XSUB)
|
||||
|
||||
// Push is a ZMQ_PUSH socket type
|
||||
Push = int(C.ZMQ_PUSH)
|
||||
|
||||
// Pull is a ZMQ_PULL socket type
|
||||
Pull = int(C.ZMQ_PULL)
|
||||
|
||||
// Pair is a ZMQ_PAIR socket type
|
||||
Pair = int(C.ZMQ_PAIR)
|
||||
|
||||
// Stream is a ZMQ_STREAM socket type
|
||||
Stream = int(C.ZMQ_STREAM)
|
||||
|
||||
// Pollin is the ZMQ_POLLIN constant
|
||||
Pollin = int(C.ZMQ_POLLIN)
|
||||
|
||||
// Pollout is the ZMQ_POLLOUT constant
|
||||
Pollout = int(C.ZMQ_POLLOUT)
|
||||
|
||||
// FlagMore is the ZFRAME_MORE flag
|
||||
FlagMore = int(C.ZFRAME_MORE)
|
||||
|
||||
// FlagReuse is the ZFRAME_REUSE flag
|
||||
FlagReuse = int(C.ZFRAME_REUSE)
|
||||
|
||||
//FlagDontWait is the ZFRAME_DONTWAIT flag
|
||||
FlagDontWait = int(C.ZFRAME_DONTWAIT)
|
||||
|
||||
//FlagNone means there are no flags
|
||||
FlagNone = 0
|
||||
|
||||
// CurveAllowAny is a semantic convenience for allowing
|
||||
// any Curve clients
|
||||
CurveAllowAny = "*"
|
||||
|
||||
//ZMQVersionMajor is the major version of the underlying ZeroMQ library
|
||||
ZMQVersionMajor = int(C.ZMQ_VERSION_MAJOR)
|
||||
|
||||
//ZMQVersionMinor is the minor version of the underlying ZeroMQ library
|
||||
ZMQVersionMinor = int(C.ZMQ_VERSION_MINOR)
|
||||
|
||||
//CZMQVersionMajor is the major version of the underlying CZMQ library
|
||||
CZMQVersionMajor = int(C.CZMQ_VERSION_MAJOR)
|
||||
|
||||
// CZMQVersionMinor is the minor version of the underlying CZMQ library
|
||||
CZMQVersionMinor = int(C.CZMQ_VERSION_MINOR)
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrActorCmd is returned when there is an error sending
|
||||
// a command to an actor
|
||||
ErrActorCmd = errors.New("error sending actor command")
|
||||
|
||||
// ErrSockAttach is returned when an attach call to a socket fails
|
||||
ErrSockAttach = errors.New("error attaching zsock")
|
||||
|
||||
// ErrInvalidSockType is returned when a function is called
|
||||
// against a socket type that is not applicable for that socket type
|
||||
ErrInvalidSockType = errors.New("invalid socket type")
|
||||
|
||||
// ErrSliceFull is returned if a []byte passed to Read was not
|
||||
// large enough to hold the contents of a message
|
||||
ErrSliceFull = errors.New("slice full")
|
||||
|
||||
// ErrConnect is returned if Connect on a socket fails
|
||||
ErrConnect = errors.New("connect error")
|
||||
|
||||
// ErrDisconnect is returned if Disconnect on a socket fails
|
||||
ErrDisconnect = errors.New("disconnect error")
|
||||
|
||||
// ErrBind is returned if Bind on a socket fails
|
||||
ErrBind = errors.New("bind error")
|
||||
|
||||
// ErrUnbind is returned if Unbind on a socket fails
|
||||
ErrUnbind = errors.New("unbind error")
|
||||
|
||||
// ErrSendFrame is returned if SendFrame on a socket fails
|
||||
ErrSendFrame = errors.New("send frame error")
|
||||
|
||||
// ErrRecvFrame is returned if RecvFrame on a socket fails
|
||||
ErrRecvFrame = errors.New("recv frame error")
|
||||
|
||||
// ErrRecvFrameAfterDestroy is returned if RecvFrame is called
|
||||
// on a socket after it has been destroyed.
|
||||
ErrRecvFrameAfterDestroy = errors.New("RecvFrame() is invalid on socket after Detroy() has been called.")
|
||||
|
||||
// ErrRecvMessage is returned if RecvMessage on a socket fails
|
||||
ErrRecvMessage = errors.New("recv message error")
|
||||
|
||||
// ErrWaitAfterDestroy is returned by a Poller if there is an error
|
||||
// accessing the underlying socket pointer when Wait is called
|
||||
ErrWaitAfterDestroy = errors.New("Wait() is invalid on Poller after Destroy() is called.")
|
||||
|
||||
// ErrMultiPartUnsupported is returned when a function that does
|
||||
// not support multi-part messages encounters a multi-part message
|
||||
ErrMultiPartUnsupported = errors.New("function does not support multi part messages")
|
||||
|
||||
// ErrTimeout is returned when a function that supports timeouts times out
|
||||
ErrTimeout = errors.New("function timed out")
|
||||
|
||||
// ErrCertNotFound is returned when NewCertFromFile tries to
|
||||
// load a file that does not exist.
|
||||
ErrCertNotFound = errors.New("file not found")
|
||||
)
|
||||
|
||||
// Shutdown shuts down the CZMQ zsys layer.
|
||||
// The CZMQ zsys layer normally shuts down on process termination through the
|
||||
// use of an atexit cleanup function. Calling this allows the zsys layer to be
|
||||
// shutdown manually.
|
||||
//
|
||||
// This is beneficial when CZMQ will no longer be used but the process will not
|
||||
// be terminating. Any potential resources allocated by the zsys layer can be
|
||||
// freed as they will no longer be needed.
|
||||
func Shutdown() {
|
||||
C.zsys_shutdown()
|
||||
}
|
||||
|
||||
func getStringType(k int) string {
|
||||
switch k {
|
||||
case Req:
|
||||
return "REQ"
|
||||
case Rep:
|
||||
return "REP"
|
||||
case Dealer:
|
||||
return "DEALER"
|
||||
case Router:
|
||||
return "ROUTER"
|
||||
case Pub:
|
||||
return "PUB"
|
||||
case Sub:
|
||||
return "SUB"
|
||||
case XPub:
|
||||
return "XPUB"
|
||||
case XSub:
|
||||
return "XSUB"
|
||||
case Push:
|
||||
return "PUSH"
|
||||
case Pull:
|
||||
return "PULL"
|
||||
case Pair:
|
||||
return "PAIR"
|
||||
case Stream:
|
||||
return "STREAM"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<license>
|
||||
Copyright (C) 2014 the Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
</license>
|
|
@ -0,0 +1,106 @@
|
|||
package goczmq
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
|
||||
zpoller_t *Poller_new(void *reader) {
|
||||
zpoller_t *poller = zpoller_new(reader, NULL);
|
||||
return poller;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Poller provides a simple wrapper to ZeroMQ's zmq_poll API,
|
||||
// for the common case of reading from a number of sockets.
|
||||
// Sockets can be added and removed from the running poller.
|
||||
type Poller struct {
|
||||
zpollerT *C.struct__zpoller_t
|
||||
socks []*Sock
|
||||
}
|
||||
|
||||
// NewPoller creates a new Poller instance.
|
||||
// It accepts one or more readers to poll.
|
||||
func NewPoller(readers ...*Sock) (*Poller, error) {
|
||||
var p *Poller
|
||||
if len(readers) == 0 {
|
||||
p = &Poller{
|
||||
zpollerT: C.Poller_new(nil),
|
||||
socks: make([]*Sock, 0),
|
||||
}
|
||||
} else {
|
||||
p = &Poller{
|
||||
zpollerT: C.Poller_new(unsafe.Pointer(readers[0].zsockT)),
|
||||
socks: make([]*Sock, 0),
|
||||
}
|
||||
|
||||
p.socks = append(p.socks, readers[0])
|
||||
if len(readers) == 1 {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
for _, reader := range readers[1:] {
|
||||
err := p.Add(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Add adds a reader to be polled.
|
||||
func (p *Poller) Add(reader *Sock) error {
|
||||
rc := C.zpoller_add(p.zpollerT, unsafe.Pointer(reader.zsockT))
|
||||
if int(rc) == -1 {
|
||||
return fmt.Errorf("error adding reader")
|
||||
}
|
||||
p.socks = append(p.socks, reader)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes a Sock from the poller
|
||||
func (p *Poller) Remove(reader *Sock) {
|
||||
numItems := len(p.socks)
|
||||
for i := 0; i < numItems; i++ {
|
||||
if p.socks[i] == reader {
|
||||
if i == numItems-1 {
|
||||
p.socks = p.socks[:i]
|
||||
} else {
|
||||
p.socks = append(p.socks[:i], p.socks[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait waits for the timeout period in milliseconds for a Pollin
|
||||
// event, and returns the first socket that returns one
|
||||
func (p *Poller) Wait(millis int) *Sock {
|
||||
if p.zpollerT == nil {
|
||||
// Null pointer. Something is wrong or we've already had `Destroy` invoked on us.
|
||||
panic(ErrWaitAfterDestroy)
|
||||
}
|
||||
s := C.zpoller_wait(p.zpollerT, C.int(millis))
|
||||
s = unsafe.Pointer(s)
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
for _, sock := range p.socks {
|
||||
if unsafe.Pointer(sock.zsockT) == s {
|
||||
return sock
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf(
|
||||
"Could not match received pointer with %v with any socket (%v)",
|
||||
s, p.socks))
|
||||
}
|
||||
|
||||
// Destroy destroys the Poller
|
||||
func (p *Poller) Destroy() {
|
||||
C.zpoller_destroy(&p.zpollerT)
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
package goczmq
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
|
||||
zactor_t *Zproxy_new () {
|
||||
zactor_t *proxy = zactor_new(zproxy, NULL);
|
||||
return proxy;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Proxy wraps the CZMQ zproxy actor. A proxy actor switches
|
||||
// messages between a frontend and backend socket, and also
|
||||
// provides an optional capture socket messages can be
|
||||
// mirrored to. The proxy can be paused and resumed.
|
||||
type Proxy struct {
|
||||
zactorT *C.struct__zactor_t
|
||||
}
|
||||
|
||||
// NewProxy creates a new Proxy instance.
|
||||
func NewProxy() *Proxy {
|
||||
p := &Proxy{}
|
||||
p.zactorT = C.Zproxy_new()
|
||||
return p
|
||||
}
|
||||
|
||||
// SetFrontend accepts a socket type and endpoint, and sends a message
|
||||
// to the zactor thread telling it to set up a socket bound to the endpoint.
|
||||
func (p *Proxy) SetFrontend(sockType int, endpoint string) error {
|
||||
typeString := getStringType(sockType)
|
||||
|
||||
cmd := C.CString("FRONTEND")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
cTypeString := C.CString(typeString)
|
||||
defer C.free(unsafe.Pointer(cTypeString))
|
||||
|
||||
cEndpoint := C.CString(endpoint)
|
||||
defer C.free(unsafe.Pointer(cEndpoint))
|
||||
|
||||
rc := C.zstr_sendm(unsafe.Pointer(p.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_sendm(unsafe.Pointer(p.zactorT), cTypeString)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_send(unsafe.Pointer(p.zactorT), cEndpoint)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zsock_wait(unsafe.Pointer(p.zactorT))
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBackend accepts a socket type and endpoint, and sends a message
|
||||
// to the zactor thread telling it to set up a socket bound to the endpoint.
|
||||
func (p *Proxy) SetBackend(sockType int, endpoint string) error {
|
||||
typeString := getStringType(sockType)
|
||||
|
||||
cmd := C.CString("BACKEND")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
cTypeString := C.CString(typeString)
|
||||
defer C.free(unsafe.Pointer(cTypeString))
|
||||
|
||||
cEndpoint := C.CString(endpoint)
|
||||
defer C.free(unsafe.Pointer(cEndpoint))
|
||||
|
||||
rc := C.zstr_sendm(unsafe.Pointer(p.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_sendm(unsafe.Pointer(p.zactorT), cTypeString)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_send(unsafe.Pointer(p.zactorT), cEndpoint)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zsock_wait(unsafe.Pointer(p.zactorT))
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetCapture accepts a socket endpoint and sets up a Push socket bound
|
||||
// to that endpoint, that sends a copy of all messages passing through
|
||||
// the proxy.
|
||||
func (p *Proxy) SetCapture(endpoint string) error {
|
||||
cmd := C.CString("CAPTURE")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
cEndpoint := C.CString(endpoint)
|
||||
defer C.free(unsafe.Pointer(cEndpoint))
|
||||
|
||||
rc := C.zstr_sendm(unsafe.Pointer(p.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
rc = C.zstr_send(unsafe.Pointer(p.zactorT), cEndpoint)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pause sends a message to the zproxy actor telling it to pause.
|
||||
func (p *Proxy) Pause() error {
|
||||
cmd := C.CString("PAUSE")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
rc := C.zstr_send(unsafe.Pointer(p.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Resume sends a message to the zproxy actor telling it to resume.
|
||||
func (p *Proxy) Resume() error {
|
||||
cmd := C.CString("RESUME")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
rc := C.zstr_send(unsafe.Pointer(p.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verbose sets the proxy to log information to stdout.
|
||||
func (p *Proxy) Verbose() error {
|
||||
cmd := C.CString("VERBOSE")
|
||||
defer C.free(unsafe.Pointer(cmd))
|
||||
|
||||
rc := C.zstr_send(unsafe.Pointer(p.zactorT), cmd)
|
||||
if rc == -1 {
|
||||
return ErrActorCmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy destroys the proxy.
|
||||
func (p *Proxy) Destroy() {
|
||||
C.zactor_destroy(&p.zactorT)
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package goczmq
|
||||
|
||||
import (
|
||||
"C"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ReadWriter provides an io.ReadWriter compatible
|
||||
// interface for goczmq.Sock
|
||||
type ReadWriter struct {
|
||||
sock *Sock
|
||||
poller *Poller
|
||||
clientIDs []string
|
||||
frame []byte
|
||||
currentIndex int
|
||||
timeoutMillis int
|
||||
}
|
||||
|
||||
// NewReadWriter accepts a sock and returns a goczmq.ReadWriter. The
|
||||
// io.ReadWriter should now be considered responsible for this
|
||||
// Sock.
|
||||
func NewReadWriter(sock *Sock) (*ReadWriter, error) {
|
||||
rw := &ReadWriter{
|
||||
sock: sock,
|
||||
timeoutMillis: -1,
|
||||
}
|
||||
|
||||
var err error
|
||||
rw.poller, err = NewPoller(rw.sock)
|
||||
return rw, err
|
||||
}
|
||||
|
||||
// SetTimeout sets the timeout on Read in millisecond. If no new
|
||||
// data is received within the timeout period, Read will return
|
||||
// an ErrTimeout
|
||||
func (r *ReadWriter) SetTimeout(ms int) {
|
||||
r.timeoutMillis = ms
|
||||
}
|
||||
|
||||
// Read satisifies io.Read
|
||||
func (r *ReadWriter) Read(p []byte) (int, error) {
|
||||
var totalRead int
|
||||
var totalFrame int
|
||||
|
||||
var flag int
|
||||
var err error
|
||||
|
||||
if r.currentIndex == 0 {
|
||||
s := r.poller.Wait(r.timeoutMillis)
|
||||
if s == nil {
|
||||
return totalRead, ErrTimeout
|
||||
}
|
||||
|
||||
r.frame, flag, err = s.RecvFrame()
|
||||
|
||||
if s.GetType() == Router && r.currentIndex == 0 {
|
||||
r.clientIDs = append(r.clientIDs, string(r.frame))
|
||||
r.frame, flag, err = s.RecvFrame()
|
||||
}
|
||||
|
||||
if flag == FlagMore {
|
||||
return totalRead, ErrMultiPartUnsupported
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return totalRead, io.EOF
|
||||
}
|
||||
}
|
||||
|
||||
totalRead += copy(p[:], r.frame[r.currentIndex:])
|
||||
totalFrame += len(r.frame)
|
||||
|
||||
if totalFrame-r.currentIndex > len(p) {
|
||||
r.currentIndex = totalRead
|
||||
} else {
|
||||
r.currentIndex = 0
|
||||
err = io.EOF
|
||||
}
|
||||
|
||||
return totalRead, err
|
||||
}
|
||||
|
||||
// Write satisfies io.Write
|
||||
func (r *ReadWriter) Write(p []byte) (int, error) {
|
||||
var total int
|
||||
if r.sock.GetType() == Router {
|
||||
err := r.sock.SendFrame(r.GetLastClientID(), FlagMore)
|
||||
if err != nil {
|
||||
return total, err
|
||||
}
|
||||
}
|
||||
err := r.sock.SendFrame(p, 0)
|
||||
if err != nil {
|
||||
return total, err
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
|
||||
}
|
||||
|
||||
// GetLastClientID returns the id of the last client you received
|
||||
// a message from if the underlying socket is a Router socket
|
||||
func (r *ReadWriter) GetLastClientID() []byte {
|
||||
id := []byte(r.clientIDs[0])
|
||||
r.clientIDs = r.clientIDs[1:]
|
||||
return id
|
||||
}
|
||||
|
||||
// SetLastClientID lets you manually set the id of the client
|
||||
// you last received a message from if the underlying socket
|
||||
// is a Router socket
|
||||
func (r *ReadWriter) SetLastClientID(id []byte) {
|
||||
r.clientIDs = append(r.clientIDs, string(id))
|
||||
}
|
||||
|
||||
// Destroy destroys both the ReadWriter and the underlying Sock
|
||||
func (r *ReadWriter) Destroy() {
|
||||
r.sock.Destroy()
|
||||
r.poller.Destroy()
|
||||
}
|
|
@ -0,0 +1,435 @@
|
|||
package goczmq
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int Sock_connect(zsock_t *self, const char *format) {return zsock_connect(self, format, NULL);}
|
||||
int Sock_disconnect(zsock_t *self, const char *format) {return zsock_disconnect(self, format, NULL);}
|
||||
int Sock_bind(zsock_t *self, const char *format) {return zsock_bind(self, format, NULL);}
|
||||
int Sock_unbind(zsock_t *self, const char *format) {return zsock_unbind(self, format, NULL);}
|
||||
int Sock_sendframe(zsock_t *sock, const void *data, size_t size, int flags) {
|
||||
zframe_t *frame = zframe_new (data, size);
|
||||
int rc = zframe_send (&frame, sock, flags);
|
||||
return rc;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Sock wraps the CZMQ zsock class.
|
||||
type Sock struct {
|
||||
zsockT *C.struct__zsock_t
|
||||
file string
|
||||
line int
|
||||
zType int
|
||||
clientIDs []string
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := os.Setenv("ZSYS_SIGHANDLER", "false"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetLastClientID returns the id of the last client you received
|
||||
// a message from if the underlying socket is a Router socket
|
||||
// DEPRECATED: See goczmq.ReadWriter
|
||||
func (s *Sock) GetLastClientID() []byte {
|
||||
id := []byte(s.clientIDs[0])
|
||||
s.clientIDs = s.clientIDs[1:]
|
||||
return id
|
||||
}
|
||||
|
||||
// SetLastClientID lets you manually set the id of the client
|
||||
// you last received a message from if the underlying socket
|
||||
// is a Router socket
|
||||
// DEPRECATED: See goczmq.ReadWriter
|
||||
func (s *Sock) SetLastClientID(id []byte) {
|
||||
s.clientIDs = append(s.clientIDs, string(id))
|
||||
}
|
||||
|
||||
// NewSock creates a new socket. The caller source and
|
||||
// line number are passed so CZMQ can report socket leaks
|
||||
// intelligently.
|
||||
func NewSock(t int) *Sock {
|
||||
var s *Sock
|
||||
_, file, line, ok := runtime.Caller(1)
|
||||
|
||||
if ok {
|
||||
s = &Sock{
|
||||
file: file,
|
||||
line: line,
|
||||
zType: t,
|
||||
clientIDs: make([]string, 0),
|
||||
}
|
||||
} else {
|
||||
s = &Sock{
|
||||
file: "",
|
||||
line: 0,
|
||||
zType: t,
|
||||
clientIDs: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
cFile := C.CString(s.file)
|
||||
defer C.free(unsafe.Pointer(cFile))
|
||||
|
||||
s.zsockT = C.zsock_new_checked(C.int(s.zType), cFile, C.size_t(s.line))
|
||||
return s
|
||||
}
|
||||
|
||||
// Connect connects a socket to an endpoint
|
||||
// returns an error if the connect failed.
|
||||
func (s *Sock) Connect(endpoint string) error {
|
||||
cEndpoint := C.CString(endpoint)
|
||||
defer C.free(unsafe.Pointer(cEndpoint))
|
||||
|
||||
rc := C.Sock_connect(s.zsockT, cEndpoint)
|
||||
if rc != C.int(0) {
|
||||
return ErrConnect
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Disconnect disconnects a socket from an endpoint. If returns
|
||||
// an error if the endpoint was not found
|
||||
func (s *Sock) Disconnect(endpoint string) error {
|
||||
cEndpoint := C.CString(endpoint)
|
||||
defer C.free(unsafe.Pointer(cEndpoint))
|
||||
|
||||
rc := C.Sock_disconnect(s.zsockT, cEndpoint)
|
||||
if int(rc) == -1 {
|
||||
return ErrDisconnect
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bind binds a socket to an endpoint. On success returns
|
||||
// the port number used for tcp transports, or 0 for other
|
||||
// transports. On failure returns a -1 for port, and an error.
|
||||
func (s *Sock) Bind(endpoint string) (int, error) {
|
||||
cEndpoint := C.CString(endpoint)
|
||||
defer C.free(unsafe.Pointer(cEndpoint))
|
||||
|
||||
port := C.Sock_bind(s.zsockT, cEndpoint)
|
||||
if port == C.int(-1) {
|
||||
return -1, ErrBind
|
||||
}
|
||||
return int(port), nil
|
||||
}
|
||||
|
||||
// Unbind unbinds a socket from an endpoint. If returns
|
||||
// an error if the endpoint was not found
|
||||
func (s *Sock) Unbind(endpoint string) error {
|
||||
cEndpoint := C.CString(endpoint)
|
||||
defer C.free(unsafe.Pointer(cEndpoint))
|
||||
|
||||
rc := C.Sock_unbind(s.zsockT, cEndpoint)
|
||||
if int(rc) == -1 {
|
||||
return ErrUnbind
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attach attaches a socket to zero or more endpoints. If endpoints is not null,
|
||||
// parses as list of ZeroMQ endpoints, separated by commas, and prefixed by
|
||||
// '@' (to bind the socket) or '>' (to attach the socket). If the endpoint
|
||||
// does not start with '@' or '>', the serverish argument determines whether
|
||||
// it is used to bind (serverish = true) or connect (serverish = false)
|
||||
func (s *Sock) Attach(endpoints string, serverish bool) error {
|
||||
cEndpoints := C.CString(endpoints)
|
||||
defer C.free(unsafe.Pointer(cEndpoints))
|
||||
|
||||
rc := C.zsock_attach(s.zsockT, cEndpoints, C._Bool(serverish))
|
||||
if rc == -1 {
|
||||
return ErrSockAttach
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewPub creates a Pub socket and calls Attach.
|
||||
// The socket will Bind by default.
|
||||
func NewPub(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Pub)
|
||||
return s, s.Attach(endpoints, true)
|
||||
}
|
||||
|
||||
// NewSub creates a Sub socket and calls Attach.
|
||||
// 'subscribe' is a comma delimited list of topics to subscribe to.
|
||||
// The socket will Connect by default.
|
||||
func NewSub(endpoints string, subscribe string) (*Sock, error) {
|
||||
s := NewSock(Sub)
|
||||
subscriptions := strings.Split(subscribe, ",")
|
||||
|
||||
for _, topic := range subscriptions {
|
||||
s.SetSubscribe(topic)
|
||||
}
|
||||
|
||||
return s, s.Attach(endpoints, false)
|
||||
}
|
||||
|
||||
// NewRep creates a Rep socket and calls Attach.
|
||||
// The socket will Bind by default.
|
||||
func NewRep(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Rep)
|
||||
return s, s.Attach(endpoints, true)
|
||||
}
|
||||
|
||||
// NewReq creates a Req socket and calls Attach.
|
||||
// The socket will Connect by default.
|
||||
func NewReq(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Req)
|
||||
return s, s.Attach(endpoints, false)
|
||||
}
|
||||
|
||||
// NewPull creates a Pull socket and calls Attach.
|
||||
// The socket will Bind by default.
|
||||
func NewPull(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Pull)
|
||||
return s, s.Attach(endpoints, true)
|
||||
}
|
||||
|
||||
// NewPush creates a Push socket and calls Attach.
|
||||
// The socket will Connect by default.
|
||||
func NewPush(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Push)
|
||||
return s, s.Attach(endpoints, false)
|
||||
}
|
||||
|
||||
// NewRouter creates a Router socket and calls Attach.
|
||||
// The socket will Bind by default.
|
||||
func NewRouter(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Router)
|
||||
return s, s.Attach(endpoints, true)
|
||||
}
|
||||
|
||||
// NewDealer creates a Dealer socket and calls Attach.
|
||||
// The socket will Connect by default.
|
||||
func NewDealer(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Dealer)
|
||||
return s, s.Attach(endpoints, false)
|
||||
}
|
||||
|
||||
// NewXPub creates an XPub socket and calls Attach.
|
||||
// The socket will Bind by default.
|
||||
func NewXPub(endpoints string) (*Sock, error) {
|
||||
s := NewSock(XPub)
|
||||
return s, s.Attach(endpoints, true)
|
||||
}
|
||||
|
||||
// NewXSub creates an XSub socket and calls Attach.
|
||||
// The socket will Connect by default.
|
||||
func NewXSub(endpoints string) (*Sock, error) {
|
||||
s := NewSock(XSub)
|
||||
return s, s.Attach(endpoints, false)
|
||||
}
|
||||
|
||||
// NewPair creates a Pair socket and calls Attach.
|
||||
// The socket will Connect by default.
|
||||
func NewPair(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Pair)
|
||||
return s, s.Attach(endpoints, false)
|
||||
}
|
||||
|
||||
// NewStream creates a Stream socket and calls Attach.
|
||||
// The socket will Connect by default.
|
||||
func NewStream(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Stream)
|
||||
return s, s.Attach(endpoints, false)
|
||||
}
|
||||
|
||||
// Pollin returns true if there is a Pollin
|
||||
// event on the socket
|
||||
func (s *Sock) Pollin() bool {
|
||||
return s.Events() == Pollin
|
||||
}
|
||||
|
||||
// Pollout returns true if there is a Pollout
|
||||
// event on the socket
|
||||
func (s *Sock) Pollout() bool {
|
||||
return s.Events() == Pollout
|
||||
}
|
||||
|
||||
// SendFrame sends a byte array via the socket. For the flags
|
||||
// value, use 0 for a single message, or SNDFlagMore if it is
|
||||
// a multi-part message
|
||||
func (s *Sock) SendFrame(data []byte, flags int) error {
|
||||
var rc C.int
|
||||
if len(data) == 0 {
|
||||
rc = C.Sock_sendframe(s.zsockT, nil, C.size_t(0), C.int(flags))
|
||||
} else {
|
||||
rc = C.Sock_sendframe(s.zsockT, unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(flags))
|
||||
}
|
||||
if rc == C.int(-1) {
|
||||
return ErrSendFrame
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecvFrame reads a frame from the socket and returns it
|
||||
// as a byte array, along with a more flag and and error
|
||||
// (if there is an error)
|
||||
func (s *Sock) RecvFrame() ([]byte, int, error) {
|
||||
if s.zsockT == nil {
|
||||
return nil, -1, ErrRecvFrameAfterDestroy
|
||||
}
|
||||
|
||||
frame := C.zframe_recv(unsafe.Pointer(s.zsockT))
|
||||
if frame == nil {
|
||||
return []byte{0}, 0, ErrRecvFrame
|
||||
}
|
||||
dataSize := C.zframe_size(frame)
|
||||
dataPtr := C.zframe_data(frame)
|
||||
b := C.GoBytes(unsafe.Pointer(dataPtr), C.int(dataSize))
|
||||
more := C.zframe_more(frame)
|
||||
C.zframe_destroy(&frame)
|
||||
return b, int(more), nil
|
||||
}
|
||||
|
||||
// RecvFrameNoWait receives a frame from the socket
|
||||
// and returns it as a byte array if one is waiting.
|
||||
// Returns an empty frame, a 0 more flag and an error
|
||||
// if one is not immediately available
|
||||
func (s *Sock) RecvFrameNoWait() ([]byte, int, error) {
|
||||
if !s.Pollin() {
|
||||
return []byte{0}, 0, ErrRecvFrame
|
||||
}
|
||||
|
||||
return s.RecvFrame()
|
||||
}
|
||||
|
||||
// SendMessage accepts an array of byte arrays and
|
||||
// sends it as a multi-part message.
|
||||
func (s *Sock) SendMessage(parts [][]byte) error {
|
||||
var f int
|
||||
numParts := len(parts)
|
||||
for i, val := range parts {
|
||||
if i == numParts-1 {
|
||||
f = 0
|
||||
} else {
|
||||
f = FlagMore
|
||||
}
|
||||
|
||||
err := s.SendFrame(val, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecvMessage receives a full message from the socket
|
||||
// and returns it as an array of byte arrays.
|
||||
func (s *Sock) RecvMessage() ([][]byte, error) {
|
||||
var msg [][]byte
|
||||
|
||||
for {
|
||||
frame, flag, err := s.RecvFrame()
|
||||
if err != nil {
|
||||
return msg, err
|
||||
}
|
||||
msg = append(msg, frame)
|
||||
if flag != FlagMore {
|
||||
break
|
||||
}
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// Read provides an io.Reader interface to a zeromq socket
|
||||
// DEPRECATED: see goczmq.ReadWriter
|
||||
func (s *Sock) Read(p []byte) (int, error) {
|
||||
var totalRead int
|
||||
var totalFrame int
|
||||
|
||||
frame, flag, err := s.RecvFrame()
|
||||
if err != nil {
|
||||
return totalRead, err
|
||||
}
|
||||
|
||||
if s.GetType() == Router {
|
||||
s.clientIDs = append(s.clientIDs, string(frame))
|
||||
} else {
|
||||
totalRead += copy(p[:], frame[:])
|
||||
totalFrame += len(frame)
|
||||
}
|
||||
|
||||
for flag == FlagMore {
|
||||
frame, flag, err = s.RecvFrame()
|
||||
if err != nil {
|
||||
return totalRead, err
|
||||
}
|
||||
totalRead += copy(p[totalRead:], frame[:])
|
||||
totalFrame += len(frame)
|
||||
}
|
||||
|
||||
if totalFrame > len(p) {
|
||||
err = ErrSliceFull
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
|
||||
return totalRead, err
|
||||
}
|
||||
|
||||
// Write provides an io.Writer interface to a zeromq socket
|
||||
// DEPRECATED: See goczmq.ReadWriter
|
||||
func (s *Sock) Write(p []byte) (int, error) {
|
||||
var total int
|
||||
if s.GetType() == Router {
|
||||
err := s.SendFrame(s.GetLastClientID(), FlagMore)
|
||||
if err != nil {
|
||||
return total, err
|
||||
}
|
||||
}
|
||||
err := s.SendFrame(p, 0)
|
||||
if err != nil {
|
||||
return total, err
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// RecvMessageNoWait receives a full message from the socket
|
||||
// and returns it as an array of byte arrays if one is waiting.
|
||||
// Returns an empty message and an error if one is not immediately
|
||||
// available
|
||||
func (s *Sock) RecvMessageNoWait() ([][]byte, error) {
|
||||
var msg [][]byte
|
||||
if !s.Pollin() {
|
||||
return msg, ErrRecvMessage
|
||||
}
|
||||
|
||||
for {
|
||||
frame, flag, err := s.RecvFrame()
|
||||
if err != nil {
|
||||
return msg, err
|
||||
}
|
||||
msg = append(msg, frame)
|
||||
if flag != FlagMore {
|
||||
break
|
||||
}
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// GetType returns the socket's type
|
||||
func (s *Sock) GetType() int {
|
||||
return s.zType
|
||||
}
|
||||
|
||||
// Destroy destroys the underlying zsockT.
|
||||
func (s *Sock) Destroy() {
|
||||
cFile := C.CString(s.file)
|
||||
defer C.free(unsafe.Pointer(cFile))
|
||||
|
||||
C.zsock_destroy_checked(&s.zsockT, cFile, C.size_t(s.line))
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// +build draft
|
||||
|
||||
package goczmq
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
// Scatter is a ZMQ_SCATTER socket type
|
||||
Scatter = int(C.ZMQ_SCATTER)
|
||||
|
||||
// Gather is a ZMQ_GATHER socket type
|
||||
Gather = int(C.ZMQ_GATHER)
|
||||
)
|
||||
|
||||
// NewGather creates a Gather socket and calls Attach.
|
||||
// The socket will Bind by default.
|
||||
func NewGather(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Gather)
|
||||
return s, s.Attach(endpoints, true)
|
||||
}
|
||||
|
||||
// NewScatter creates a Scatter socket and calls Attach.
|
||||
// The socket will Connect by default.
|
||||
func NewScatter(endpoints string) (*Sock, error) {
|
||||
s := NewSock(Scatter)
|
||||
return s, s.Attach(endpoints, false)
|
||||
}
|
|
@ -0,0 +1,765 @@
|
|||
//go:generate gsl sockopts.xml
|
||||
package goczmq
|
||||
|
||||
/* =========================================================================
|
||||
zsock_option - get/set 0MQ socket options
|
||||
|
||||
****************************************************
|
||||
* GENERATED SOURCE CODE, DO NOT EDIT!! *
|
||||
* TO CHANGE THIS, EDIT sockopts.gsl *
|
||||
* AND RUN gsl -q sockopts.xml *
|
||||
****************************************************
|
||||
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
This file is part of goczmq, the high-level go binding for CZMQ:
|
||||
http://github.com/zeromq/goczmq
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SetHeartbeatIvl sets the heartbeat_ivl option for the socket
|
||||
func (s *Sock) SetHeartbeatIvl(val int) {
|
||||
C.zsock_set_heartbeat_ivl(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// HeartbeatIvl returns the current value of the socket's heartbeat_ivl option
|
||||
func (s *Sock) HeartbeatIvl() int {
|
||||
val := C.zsock_heartbeat_ivl(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetHeartbeatTtl sets the heartbeat_ttl option for the socket
|
||||
func (s *Sock) SetHeartbeatTtl(val int) {
|
||||
C.zsock_set_heartbeat_ttl(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// HeartbeatTtl returns the current value of the socket's heartbeat_ttl option
|
||||
func (s *Sock) HeartbeatTtl() int {
|
||||
val := C.zsock_heartbeat_ttl(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetHeartbeatTimeout sets the heartbeat_timeout option for the socket
|
||||
func (s *Sock) SetHeartbeatTimeout(val int) {
|
||||
C.zsock_set_heartbeat_timeout(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// HeartbeatTimeout returns the current value of the socket's heartbeat_timeout option
|
||||
func (s *Sock) HeartbeatTimeout() int {
|
||||
val := C.zsock_heartbeat_timeout(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetUseFd sets the use_fd option for the socket
|
||||
func (s *Sock) SetUseFd(val int) {
|
||||
C.zsock_set_use_fd(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// UseFd returns the current value of the socket's use_fd option
|
||||
func (s *Sock) UseFd() int {
|
||||
val := C.zsock_use_fd(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetXPubManual sets the xpub_manual option for the socket
|
||||
func (s *Sock) SetXPubManual(val int) {
|
||||
C.zsock_set_xpub_manual(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetXPubWelcomeMsg sets the xpub_welcome_msg option for the socket
|
||||
func (s *Sock) SetXPubWelcomeMsg(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_xpub_welcome_msg(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// SetStreamNotify sets the stream_notify option for the socket
|
||||
func (s *Sock) SetStreamNotify(val int) {
|
||||
C.zsock_set_stream_notify(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetInvertMatching sets the invert_matching option for the socket
|
||||
func (s *Sock) SetInvertMatching(val int) {
|
||||
C.zsock_set_invert_matching(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// InvertMatching returns the current value of the socket's invert_matching option
|
||||
func (s *Sock) InvertMatching() int {
|
||||
val := C.zsock_invert_matching(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetXPubVerboser sets the xpub_verboser option for the socket
|
||||
func (s *Sock) SetXPubVerboser(val int) {
|
||||
C.zsock_set_xpub_verboser(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetConnectTimeout sets the connect_timeout option for the socket
|
||||
func (s *Sock) SetConnectTimeout(val int) {
|
||||
C.zsock_set_connect_timeout(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// ConnectTimeout returns the current value of the socket's connect_timeout option
|
||||
func (s *Sock) ConnectTimeout() int {
|
||||
val := C.zsock_connect_timeout(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetTcpMaxrt sets the tcp_maxrt option for the socket
|
||||
func (s *Sock) SetTcpMaxrt(val int) {
|
||||
C.zsock_set_tcp_maxrt(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// TcpMaxrt returns the current value of the socket's tcp_maxrt option
|
||||
func (s *Sock) TcpMaxrt() int {
|
||||
val := C.zsock_tcp_maxrt(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// ThreadSafe returns the current value of the socket's thread_safe option
|
||||
func (s *Sock) ThreadSafe() int {
|
||||
val := C.zsock_thread_safe(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetMulticastMaxtpdu sets the multicast_maxtpdu option for the socket
|
||||
func (s *Sock) SetMulticastMaxtpdu(val int) {
|
||||
C.zsock_set_multicast_maxtpdu(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// MulticastMaxtpdu returns the current value of the socket's multicast_maxtpdu option
|
||||
func (s *Sock) MulticastMaxtpdu() int {
|
||||
val := C.zsock_multicast_maxtpdu(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetVmciBufferSize sets the vmci_buffer_size option for the socket
|
||||
func (s *Sock) SetVmciBufferSize(val int) {
|
||||
C.zsock_set_vmci_buffer_size(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// VmciBufferSize returns the current value of the socket's vmci_buffer_size option
|
||||
func (s *Sock) VmciBufferSize() int {
|
||||
val := C.zsock_vmci_buffer_size(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetVmciBufferMinSize sets the vmci_buffer_min_size option for the socket
|
||||
func (s *Sock) SetVmciBufferMinSize(val int) {
|
||||
C.zsock_set_vmci_buffer_min_size(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// VmciBufferMinSize returns the current value of the socket's vmci_buffer_min_size option
|
||||
func (s *Sock) VmciBufferMinSize() int {
|
||||
val := C.zsock_vmci_buffer_min_size(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetVmciBufferMaxSize sets the vmci_buffer_max_size option for the socket
|
||||
func (s *Sock) SetVmciBufferMaxSize(val int) {
|
||||
C.zsock_set_vmci_buffer_max_size(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// VmciBufferMaxSize returns the current value of the socket's vmci_buffer_max_size option
|
||||
func (s *Sock) VmciBufferMaxSize() int {
|
||||
val := C.zsock_vmci_buffer_max_size(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetVmciConnectTimeout sets the vmci_connect_timeout option for the socket
|
||||
func (s *Sock) SetVmciConnectTimeout(val int) {
|
||||
C.zsock_set_vmci_connect_timeout(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// VmciConnectTimeout returns the current value of the socket's vmci_connect_timeout option
|
||||
func (s *Sock) VmciConnectTimeout() int {
|
||||
val := C.zsock_vmci_connect_timeout(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetTos sets the tos option for the socket
|
||||
func (s *Sock) SetTos(val int) {
|
||||
C.zsock_set_tos(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Tos returns the current value of the socket's tos option
|
||||
func (s *Sock) Tos() int {
|
||||
val := C.zsock_tos(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetRouterHandover sets the router_handover option for the socket
|
||||
func (s *Sock) SetRouterHandover(val int) {
|
||||
C.zsock_set_router_handover(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetConnectRid sets the connect_rid option for the socket
|
||||
func (s *Sock) SetConnectRid(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_connect_rid(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// SetHandshakeIvl sets the handshake_ivl option for the socket
|
||||
func (s *Sock) SetHandshakeIvl(val int) {
|
||||
C.zsock_set_handshake_ivl(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// HandshakeIvl returns the current value of the socket's handshake_ivl option
|
||||
func (s *Sock) HandshakeIvl() int {
|
||||
val := C.zsock_handshake_ivl(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetSocksProxy sets the socks_proxy option for the socket
|
||||
func (s *Sock) SetSocksProxy(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_socks_proxy(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// SocksProxy returns the current value of the socket's socks_proxy option
|
||||
func (s *Sock) SocksProxy() string {
|
||||
val := C.zsock_socks_proxy(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// SetXPubNodrop sets the xpub_nodrop option for the socket
|
||||
func (s *Sock) SetXPubNodrop(val int) {
|
||||
C.zsock_set_xpub_nodrop(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetRouterMandatory sets the router_mandatory option for the socket
|
||||
func (s *Sock) SetRouterMandatory(val int) {
|
||||
C.zsock_set_router_mandatory(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetProbeRouter sets the probe_router option for the socket
|
||||
func (s *Sock) SetProbeRouter(val int) {
|
||||
C.zsock_set_probe_router(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetReqRelaxed sets the req_relaxed option for the socket
|
||||
func (s *Sock) SetReqRelaxed(val int) {
|
||||
C.zsock_set_req_relaxed(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetReqCorrelate sets the req_correlate option for the socket
|
||||
func (s *Sock) SetReqCorrelate(val int) {
|
||||
C.zsock_set_req_correlate(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetConflate sets the conflate option for the socket
|
||||
func (s *Sock) SetConflate(val int) {
|
||||
C.zsock_set_conflate(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetZapDomain sets the zap_domain option for the socket
|
||||
func (s *Sock) SetZapDomain(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_zap_domain(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// ZapDomain returns the current value of the socket's zap_domain option
|
||||
func (s *Sock) ZapDomain() string {
|
||||
val := C.zsock_zap_domain(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// Mechanism returns the current value of the socket's mechanism option
|
||||
func (s *Sock) Mechanism() int {
|
||||
val := C.zsock_mechanism(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetPlainServer sets the plain_server option for the socket
|
||||
func (s *Sock) SetPlainServer(val int) {
|
||||
C.zsock_set_plain_server(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// PlainServer returns the current value of the socket's plain_server option
|
||||
func (s *Sock) PlainServer() int {
|
||||
val := C.zsock_plain_server(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetPlainUsername sets the plain_username option for the socket
|
||||
func (s *Sock) SetPlainUsername(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_plain_username(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// PlainUsername returns the current value of the socket's plain_username option
|
||||
func (s *Sock) PlainUsername() string {
|
||||
val := C.zsock_plain_username(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// SetPlainPassword sets the plain_password option for the socket
|
||||
func (s *Sock) SetPlainPassword(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_plain_password(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// PlainPassword returns the current value of the socket's plain_password option
|
||||
func (s *Sock) PlainPassword() string {
|
||||
val := C.zsock_plain_password(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// SetCurveServer sets the curve_server option for the socket
|
||||
func (s *Sock) SetCurveServer(val int) {
|
||||
C.zsock_set_curve_server(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// CurveServer returns the current value of the socket's curve_server option
|
||||
func (s *Sock) CurveServer() int {
|
||||
val := C.zsock_curve_server(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetCurvePublickey sets the curve_publickey option for the socket
|
||||
func (s *Sock) SetCurvePublickey(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_curve_publickey(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// CurvePublickey returns the current value of the socket's curve_publickey option
|
||||
func (s *Sock) CurvePublickey() string {
|
||||
val := C.zsock_curve_publickey(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// SetCurveSecretkey sets the curve_secretkey option for the socket
|
||||
func (s *Sock) SetCurveSecretkey(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_curve_secretkey(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// CurveSecretkey returns the current value of the socket's curve_secretkey option
|
||||
func (s *Sock) CurveSecretkey() string {
|
||||
val := C.zsock_curve_secretkey(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// SetCurveServerkey sets the curve_serverkey option for the socket
|
||||
func (s *Sock) SetCurveServerkey(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_curve_serverkey(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// CurveServerkey returns the current value of the socket's curve_serverkey option
|
||||
func (s *Sock) CurveServerkey() string {
|
||||
val := C.zsock_curve_serverkey(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// SetGssapiServer sets the gssapi_server option for the socket
|
||||
func (s *Sock) SetGssapiServer(val int) {
|
||||
C.zsock_set_gssapi_server(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// GssapiServer returns the current value of the socket's gssapi_server option
|
||||
func (s *Sock) GssapiServer() int {
|
||||
val := C.zsock_gssapi_server(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetGssapiPlaintext sets the gssapi_plaintext option for the socket
|
||||
func (s *Sock) SetGssapiPlaintext(val int) {
|
||||
C.zsock_set_gssapi_plaintext(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// GssapiPlaintext returns the current value of the socket's gssapi_plaintext option
|
||||
func (s *Sock) GssapiPlaintext() int {
|
||||
val := C.zsock_gssapi_plaintext(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetGssapiPrincipal sets the gssapi_principal option for the socket
|
||||
func (s *Sock) SetGssapiPrincipal(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_gssapi_principal(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// GssapiPrincipal returns the current value of the socket's gssapi_principal option
|
||||
func (s *Sock) GssapiPrincipal() string {
|
||||
val := C.zsock_gssapi_principal(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// SetGssapiServicePrincipal sets the gssapi_service_principal option for the socket
|
||||
func (s *Sock) SetGssapiServicePrincipal(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_gssapi_service_principal(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// GssapiServicePrincipal returns the current value of the socket's gssapi_service_principal option
|
||||
func (s *Sock) GssapiServicePrincipal() string {
|
||||
val := C.zsock_gssapi_service_principal(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// SetIpv6 sets the ipv6 option for the socket
|
||||
func (s *Sock) SetIpv6(val int) {
|
||||
C.zsock_set_ipv6(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Ipv6 returns the current value of the socket's ipv6 option
|
||||
func (s *Sock) Ipv6() int {
|
||||
val := C.zsock_ipv6(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetImmediate sets the immediate option for the socket
|
||||
func (s *Sock) SetImmediate(val int) {
|
||||
C.zsock_set_immediate(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Immediate returns the current value of the socket's immediate option
|
||||
func (s *Sock) Immediate() int {
|
||||
val := C.zsock_immediate(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetRouterRaw sets the router_raw option for the socket
|
||||
func (s *Sock) SetRouterRaw(val int) {
|
||||
C.zsock_set_router_raw(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetIpv4only sets the ipv4only option for the socket
|
||||
func (s *Sock) SetIpv4only(val int) {
|
||||
C.zsock_set_ipv4only(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Ipv4only returns the current value of the socket's ipv4only option
|
||||
func (s *Sock) Ipv4only() int {
|
||||
val := C.zsock_ipv4only(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetDelayAttachOnConnect sets the delay_attach_on_connect option for the socket
|
||||
func (s *Sock) SetDelayAttachOnConnect(val int) {
|
||||
C.zsock_set_delay_attach_on_connect(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Type returns the current value of the socket's type option
|
||||
func (s *Sock) Type() int {
|
||||
val := C.zsock_type(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetSndhwm sets the sndhwm option for the socket
|
||||
func (s *Sock) SetSndhwm(val int) {
|
||||
C.zsock_set_sndhwm(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Sndhwm returns the current value of the socket's sndhwm option
|
||||
func (s *Sock) Sndhwm() int {
|
||||
val := C.zsock_sndhwm(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetRcvhwm sets the rcvhwm option for the socket
|
||||
func (s *Sock) SetRcvhwm(val int) {
|
||||
C.zsock_set_rcvhwm(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Rcvhwm returns the current value of the socket's rcvhwm option
|
||||
func (s *Sock) Rcvhwm() int {
|
||||
val := C.zsock_rcvhwm(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetAffinity sets the affinity option for the socket
|
||||
func (s *Sock) SetAffinity(val int) {
|
||||
C.zsock_set_affinity(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Affinity returns the current value of the socket's affinity option
|
||||
func (s *Sock) Affinity() int {
|
||||
val := C.zsock_affinity(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetSubscribe sets the subscribe option for the socket
|
||||
func (s *Sock) SetSubscribe(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_subscribe(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// SetUnsubscribe sets the unsubscribe option for the socket
|
||||
func (s *Sock) SetUnsubscribe(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_unsubscribe(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// SetIdentity sets the identity option for the socket
|
||||
func (s *Sock) SetIdentity(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_identity(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// Identity returns the current value of the socket's identity option
|
||||
func (s *Sock) Identity() string {
|
||||
val := C.zsock_identity(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// SetRate sets the rate option for the socket
|
||||
func (s *Sock) SetRate(val int) {
|
||||
C.zsock_set_rate(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Rate returns the current value of the socket's rate option
|
||||
func (s *Sock) Rate() int {
|
||||
val := C.zsock_rate(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetRecoveryIvl sets the recovery_ivl option for the socket
|
||||
func (s *Sock) SetRecoveryIvl(val int) {
|
||||
C.zsock_set_recovery_ivl(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// RecoveryIvl returns the current value of the socket's recovery_ivl option
|
||||
func (s *Sock) RecoveryIvl() int {
|
||||
val := C.zsock_recovery_ivl(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetSndbuf sets the sndbuf option for the socket
|
||||
func (s *Sock) SetSndbuf(val int) {
|
||||
C.zsock_set_sndbuf(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Sndbuf returns the current value of the socket's sndbuf option
|
||||
func (s *Sock) Sndbuf() int {
|
||||
val := C.zsock_sndbuf(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetRcvbuf sets the rcvbuf option for the socket
|
||||
func (s *Sock) SetRcvbuf(val int) {
|
||||
C.zsock_set_rcvbuf(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Rcvbuf returns the current value of the socket's rcvbuf option
|
||||
func (s *Sock) Rcvbuf() int {
|
||||
val := C.zsock_rcvbuf(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetLinger sets the linger option for the socket
|
||||
func (s *Sock) SetLinger(val int) {
|
||||
C.zsock_set_linger(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Linger returns the current value of the socket's linger option
|
||||
func (s *Sock) Linger() int {
|
||||
val := C.zsock_linger(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetReconnectIvl sets the reconnect_ivl option for the socket
|
||||
func (s *Sock) SetReconnectIvl(val int) {
|
||||
C.zsock_set_reconnect_ivl(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// ReconnectIvl returns the current value of the socket's reconnect_ivl option
|
||||
func (s *Sock) ReconnectIvl() int {
|
||||
val := C.zsock_reconnect_ivl(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetReconnectIvlMax sets the reconnect_ivl_max option for the socket
|
||||
func (s *Sock) SetReconnectIvlMax(val int) {
|
||||
C.zsock_set_reconnect_ivl_max(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// ReconnectIvlMax returns the current value of the socket's reconnect_ivl_max option
|
||||
func (s *Sock) ReconnectIvlMax() int {
|
||||
val := C.zsock_reconnect_ivl_max(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetBacklog sets the backlog option for the socket
|
||||
func (s *Sock) SetBacklog(val int) {
|
||||
C.zsock_set_backlog(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Backlog returns the current value of the socket's backlog option
|
||||
func (s *Sock) Backlog() int {
|
||||
val := C.zsock_backlog(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetMaxmsgsize sets the maxmsgsize option for the socket
|
||||
func (s *Sock) SetMaxmsgsize(val int) {
|
||||
C.zsock_set_maxmsgsize(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Maxmsgsize returns the current value of the socket's maxmsgsize option
|
||||
func (s *Sock) Maxmsgsize() int {
|
||||
val := C.zsock_maxmsgsize(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetMulticastHops sets the multicast_hops option for the socket
|
||||
func (s *Sock) SetMulticastHops(val int) {
|
||||
C.zsock_set_multicast_hops(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// MulticastHops returns the current value of the socket's multicast_hops option
|
||||
func (s *Sock) MulticastHops() int {
|
||||
val := C.zsock_multicast_hops(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetRcvtimeo sets the rcvtimeo option for the socket
|
||||
func (s *Sock) SetRcvtimeo(val int) {
|
||||
C.zsock_set_rcvtimeo(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Rcvtimeo returns the current value of the socket's rcvtimeo option
|
||||
func (s *Sock) Rcvtimeo() int {
|
||||
val := C.zsock_rcvtimeo(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetSndtimeo sets the sndtimeo option for the socket
|
||||
func (s *Sock) SetSndtimeo(val int) {
|
||||
C.zsock_set_sndtimeo(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// Sndtimeo returns the current value of the socket's sndtimeo option
|
||||
func (s *Sock) Sndtimeo() int {
|
||||
val := C.zsock_sndtimeo(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetXPubVerbose sets the xpub_verbose option for the socket
|
||||
func (s *Sock) SetXPubVerbose(val int) {
|
||||
C.zsock_set_xpub_verbose(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// SetTcpKeepalive sets the tcp_keepalive option for the socket
|
||||
func (s *Sock) SetTcpKeepalive(val int) {
|
||||
C.zsock_set_tcp_keepalive(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// TcpKeepalive returns the current value of the socket's tcp_keepalive option
|
||||
func (s *Sock) TcpKeepalive() int {
|
||||
val := C.zsock_tcp_keepalive(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetTcpKeepaliveIdle sets the tcp_keepalive_idle option for the socket
|
||||
func (s *Sock) SetTcpKeepaliveIdle(val int) {
|
||||
C.zsock_set_tcp_keepalive_idle(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// TcpKeepaliveIdle returns the current value of the socket's tcp_keepalive_idle option
|
||||
func (s *Sock) TcpKeepaliveIdle() int {
|
||||
val := C.zsock_tcp_keepalive_idle(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetTcpKeepaliveCnt sets the tcp_keepalive_cnt option for the socket
|
||||
func (s *Sock) SetTcpKeepaliveCnt(val int) {
|
||||
C.zsock_set_tcp_keepalive_cnt(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// TcpKeepaliveCnt returns the current value of the socket's tcp_keepalive_cnt option
|
||||
func (s *Sock) TcpKeepaliveCnt() int {
|
||||
val := C.zsock_tcp_keepalive_cnt(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetTcpKeepaliveIntvl sets the tcp_keepalive_intvl option for the socket
|
||||
func (s *Sock) SetTcpKeepaliveIntvl(val int) {
|
||||
C.zsock_set_tcp_keepalive_intvl(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
// TcpKeepaliveIntvl returns the current value of the socket's tcp_keepalive_intvl option
|
||||
func (s *Sock) TcpKeepaliveIntvl() int {
|
||||
val := C.zsock_tcp_keepalive_intvl(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// SetTcpAcceptFilter sets the tcp_accept_filter option for the socket
|
||||
func (s *Sock) SetTcpAcceptFilter(val string) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_tcp_accept_filter(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
// TcpAcceptFilter returns the current value of the socket's tcp_accept_filter option
|
||||
func (s *Sock) TcpAcceptFilter() string {
|
||||
val := C.zsock_tcp_accept_filter(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
// Rcvmore returns the current value of the socket's rcvmore option
|
||||
func (s *Sock) Rcvmore() int {
|
||||
val := C.zsock_rcvmore(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// Fd returns the current value of the socket's fd option
|
||||
func (s *Sock) Fd() int {
|
||||
val := C.zsock_fd(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// Events returns the current value of the socket's events option
|
||||
func (s *Sock) Events() int {
|
||||
val := C.zsock_events(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
// LastEndpoint returns the current value of the socket's last_endpoint option
|
||||
func (s *Sock) LastEndpoint() string {
|
||||
val := C.zsock_last_endpoint(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
.# This is a code generator built using the iMatix GSL code generation
|
||||
.# language. See https://github.com/imatix/gsl for details. This script
|
||||
.# is licensed under MIT/X11.
|
||||
.#
|
||||
.output "./sock_option.go"
|
||||
//go:generate gsl sockopts.xml
|
||||
package goczmq
|
||||
|
||||
/* =========================================================================
|
||||
zsock_option - get/set 0MQ socket options
|
||||
|
||||
****************************************************
|
||||
* GENERATED SOURCE CODE, DO NOT EDIT!! *
|
||||
* TO CHANGE THIS, EDIT sockopts.gsl *
|
||||
* AND RUN gsl -q sockopts.xml *
|
||||
****************************************************
|
||||
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
This file is part of goczmq, the high-level go binding for CZMQ:
|
||||
http://github.com/zeromq/goczmq
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
#include "czmq.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
.for version
|
||||
.if major = "4"
|
||||
.for option
|
||||
.if mode = "rw" | mode = "w"
|
||||
.if type = "uint64" | type = "int64" | type = "uint32" | type = "int"
|
||||
// Set$(name:pascal) sets the $(name) option for the socket
|
||||
func (s *Sock) Set$(name:pascal)(val $(gotype)) {
|
||||
C.zsock_set_$(name)(unsafe.Pointer(s.zsockT), C.int(val))
|
||||
}
|
||||
|
||||
.endif
|
||||
.if type = "string" | type = "key"
|
||||
// Set$(name:pascal) sets the $(name) option for the socket
|
||||
func (s *Sock) Set$(name:pascal)(val $(gotype)) {
|
||||
cVal := C.CString(val)
|
||||
defer C.free(unsafe.Pointer(cVal))
|
||||
|
||||
C.zsock_set_$(name)(unsafe.Pointer(s.zsockT), cVal)
|
||||
}
|
||||
|
||||
.endif
|
||||
.endif
|
||||
.if mode = "rw" | mode = "r"
|
||||
.if type = "uint64" | type = "int64" | type = "uint32" | type = "int"
|
||||
// $(name:pascal) returns the current value of the socket's $(name) option
|
||||
func (s *Sock) $(name:pascal)() $(gotype) {
|
||||
val := C.zsock_$(name)(unsafe.Pointer(s.zsockT))
|
||||
return int(val)
|
||||
}
|
||||
|
||||
.endif
|
||||
.if type = "string" | type = "key"
|
||||
// $(name:pascal) returns the current value of the socket's $(name) option
|
||||
func (s *Sock) $(name:pascal)() $(gotype) {
|
||||
val := C.zsock_$(name)(unsafe.Pointer(s.zsockT))
|
||||
return C.GoString(val)
|
||||
}
|
||||
|
||||
.endif
|
||||
.endif
|
||||
.endfor
|
||||
.endif
|
||||
.for source
|
||||
$(string.trim(.):)
|
||||
.endfor
|
||||
.endfor
|
|
@ -0,0 +1,83 @@
|
|||
.# This is a code generator built using the iMatix GSL code generation
|
||||
.# language. See https://github.com/imatix/gsl for details. This script
|
||||
.# is licensed under MIT/X11.
|
||||
.#
|
||||
.output "./sock_option_test.go"
|
||||
//go:generate gsl sockopts.xml
|
||||
package goczmq
|
||||
/* =========================================================================
|
||||
zsock_option - get/set 0MQ socket options
|
||||
|
||||
****************************************************
|
||||
* GENERATED SOURCE CODE, DO NOT EDIT!! *
|
||||
****************************************************
|
||||
|
||||
Copyright (c) the Contributors as noted in the AUTHORS file.
|
||||
This file is part of goczmq, the high-level go binding for CZMQ:
|
||||
http://github.com/zeromq/goczmq
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
.for version
|
||||
.if major = "4"
|
||||
.for option where defined(test)
|
||||
.if mode = "rw" | mode = "w"
|
||||
.if type = "uint64" | type = "int64" | type = "uint32" | type = "int"
|
||||
func Test$(name:pascal)(t *testing.T) {
|
||||
sock := NewSock($(test:neat))
|
||||
testval := $(test_value?'1':)
|
||||
sock.Set$(name:pascal)(testval)
|
||||
.if mode = "rw"
|
||||
val := sock.$(name:pascal)()
|
||||
if val != testval && val != 0 {
|
||||
t.Errorf("$(name:pascal) returned %d, should be %d", val, testval)
|
||||
}
|
||||
.endif
|
||||
sock.Destroy()
|
||||
}
|
||||
|
||||
.endif
|
||||
.if type = "string" | type = "key"
|
||||
func Test$(name:pascal)(t *testing.T) {
|
||||
sock := NewSock($(test:neat))
|
||||
testval := "$(test_value?'test':)"
|
||||
sock.Set$(name:pascal)(testval)
|
||||
.if mode = "rw"
|
||||
val := sock.$(name:pascal)()
|
||||
if val != testval && val != "" {
|
||||
t.Errorf("$(name:pascal) returned %s should be %s", val, testval)
|
||||
}
|
||||
.endif
|
||||
sock.Destroy()
|
||||
}
|
||||
|
||||
.endif
|
||||
.if mode = "r"
|
||||
.if type = "uint64" | type = "int64" | type = "uint32" | type = "int"
|
||||
func Test$(name:pascal)(t *testing.T) {
|
||||
sock := NewSock($(test:neat))
|
||||
_ = sock.$(name:pascal)()
|
||||
sock.Destroy()
|
||||
}
|
||||
|
||||
.endif
|
||||
.if type = "string" | type = "key"
|
||||
func Test$(name:pascal)(t *testing.T) {
|
||||
sock := NewSock($(test:pascal))
|
||||
_ = sock.$(name:pascal)()
|
||||
sock.Destroy()
|
||||
}
|
||||
|
||||
.endif
|
||||
.endif
|
||||
.endif
|
||||
.endfor
|
||||
.endif
|
||||
.endfor
|
|
@ -0,0 +1,32 @@
|
|||
.# This is a code generator built using the iMatix GSL code generation
|
||||
.# language. See https://github.com/imatix/gsl for details. This script
|
||||
.# is licensed under MIT/X11.
|
||||
.#
|
||||
.template 0
|
||||
for version
|
||||
# Expand any macros
|
||||
for include
|
||||
for options.macro where name = include.name
|
||||
for . as child
|
||||
copy child to version
|
||||
endfor
|
||||
endfor
|
||||
endfor
|
||||
# Preprocess options
|
||||
for option
|
||||
if type = "uint64" | type = "int64" | type = "uint32" | type = "int"
|
||||
option.ctype = "int"
|
||||
option.ctype_const = "int"
|
||||
option.gotype = "int"
|
||||
elsif type = "string" | type = "key"
|
||||
option.ctype = "char *" # Enforce C strings
|
||||
option.ctype_const = "const char *"
|
||||
option.gotype = "string"
|
||||
else
|
||||
echo "E: unknown type: $(type)"
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
.endtemplate
|
||||
.include "sock_option.gsl"
|
||||
.include "sock_option_test.gsl"
|
|
@ -0,0 +1,233 @@
|
|||
<?xml?>
|
||||
<!-- Used to generate the socket options interface
|
||||
Requires gsl4 from https://github.com/imatix/gsl
|
||||
use 'gsl sockopts'
|
||||
-->
|
||||
|
||||
<options script = "sockopts">
|
||||
<version major = "4" minor = "2" style = "macro">
|
||||
<!-- Options that are new in 4.2 -->
|
||||
<option name = "heartbeat_ivl" type = "int" mode = "rw" test = "DEALER"
|
||||
test_value = "2000" />
|
||||
<option name = "heartbeat_ttl" type = "int" mode = "rw" test = "DEALER"
|
||||
test_value = "4000" />
|
||||
<option name = "heartbeat_timeout" type = "int" mode = "rw" test = "DEALER"
|
||||
test_value = "6000" />
|
||||
<option name = "use_fd" type = "int" mode = "rw" test = "REQ"
|
||||
test_value = "3" />
|
||||
<option name = "xpub_manual" type = "int" mode = "w" test = "XPUB"
|
||||
test_value = "1" >
|
||||
<restrict type = "XPUB" />
|
||||
</option>
|
||||
<option name = "xpub_welcome_msg" type = "string" mode = "w" test = "XPUB"
|
||||
test_value = "welcome" >
|
||||
<restrict type = "XPUB" />
|
||||
</option>
|
||||
<option name = "stream_notify" type = "int" mode = "w" test = "STREAM"
|
||||
test_value = "1" >
|
||||
<restrict type = "STREAM" />
|
||||
</option>
|
||||
<option name = "invert_matching" type = "int" mode = "rw" test = "XPUB"
|
||||
test_value = "1" >
|
||||
<restrict type = "XPUB" />
|
||||
<restrict type = "PUB" />
|
||||
<restrict type = "SUB" />
|
||||
</option>
|
||||
<option name = "xpub_verboser" type = "int" mode = "w" test = "XPUB"
|
||||
test_value = "1" >
|
||||
<restrict type = "XPUB" />
|
||||
</option>
|
||||
<option name = "connect_timeout" type = "int" mode = "rw" test = "DEALER"
|
||||
test_value = "200" />
|
||||
<option name = "tcp_maxrt" type = "int" mode = "rw" test = "DEALER"
|
||||
test_value = "200" />
|
||||
<option name = "thread_safe" type = "int" mode = "r" test = "DEALER"
|
||||
test_value = "0" />
|
||||
<option name = "multicast_maxtpdu" type = "int" mode = "rw" test = "DEALER"
|
||||
test_value = "1400" />
|
||||
|
||||
<!-- We don't test these as libzmq doesn't always support VMCI -->
|
||||
<option name = "vmci_buffer_size" type = "uint64" mode = "rw" />
|
||||
<option name = "vmci_buffer_min_size" type = "uint64" mode = "rw" />
|
||||
<option name = "vmci_buffer_max_size" type = "uint64" mode = "rw" />
|
||||
<option name = "vmci_connect_timeout" type = "int" mode = "rw" />
|
||||
</version>
|
||||
|
||||
<version major = "4" minor = "1" style = "macro">
|
||||
<!-- Options that are new in 4.1 -->
|
||||
<option name = "tos" type = "int" mode = "rw" test = "DEALER" />
|
||||
<option name = "router_handover" type = "int" mode = "w" test = "ROUTER">
|
||||
<restrict type = "ROUTER" />
|
||||
</option>
|
||||
<option name = "connect_rid" type = "key" mode = "w" test = "ROUTER"
|
||||
test_value = "ABCD" >
|
||||
<restrict type = "ROUTER" />
|
||||
<restrict type = "STREAM" />
|
||||
</option>
|
||||
<option name = "handshake_ivl" type = "int" mode = "rw" test = "DEALER"
|
||||
test_value = "200" />
|
||||
<option name = "socks_proxy" type = "string" mode = "rw" test = "DEALER"
|
||||
test_value = "127.0.0.1" />
|
||||
<option name = "xpub_nodrop" type = "int" mode = "w" test = "XPUB"
|
||||
test_value = "1" >
|
||||
<restrict type = "XPUB" />
|
||||
<restrict type = "PUB" />
|
||||
</option>
|
||||
</version>
|
||||
|
||||
<version major = "4" style = "macro">
|
||||
<!-- Options that are new in 4.1 -->
|
||||
<option name = "tos" type = "int" mode = "rw" test = "Dealer" />
|
||||
<option name = "router_handover" type = "int" mode = "w" test = "Router">
|
||||
<restrict type = "Router" />
|
||||
</option>
|
||||
|
||||
<!-- Options that are new in 4.0 -->
|
||||
<option name = "router_mandatory" type = "int" mode = "w" test = "Router">
|
||||
<restrict type = "Router" />
|
||||
</option>
|
||||
<option name = "probe_router" type = "int" mode = "w" test = "Dealer">
|
||||
<restrict type = "Router" />
|
||||
<restrict type = "Dealer" />
|
||||
<restrict type = "Req" />
|
||||
</option>
|
||||
<option name = "req_relaxed" type = "int" mode = "w" test = "Req">
|
||||
<restrict type = "Req" />
|
||||
</option>
|
||||
<option name = "req_correlate" type = "int" mode = "w" test = "Req">
|
||||
<restrict type = "Req" />
|
||||
</option>
|
||||
<option name = "conflate" type = "int" mode = "w" test = "Push">
|
||||
<restrict type = "Push" />
|
||||
<restrict type = "Pull" />
|
||||
<restrict type = "Pub" />
|
||||
<restrict type = "Sub" />
|
||||
<restrict type = "Dealer" />
|
||||
</option>
|
||||
|
||||
<!-- Security options -->
|
||||
<option name = "zap_domain" type = "string" mode = "rw" test = "Sub" />
|
||||
<option name = "mechanism" type = "int" mode = "r" test = "Sub" />
|
||||
|
||||
<option name = "plain_server" type = "int" mode = "rw" test = "Pub" />
|
||||
<option name = "plain_username" type = "string" mode = "rw" test = "Sub" />
|
||||
<option name = "plain_password" type = "string" mode = "rw" test = "Sub" />
|
||||
|
||||
<!-- We don't test these as libzmq doesn't always support CURVE security -->
|
||||
<option name = "curve_server" type = "int" mode = "rw" />
|
||||
<option name = "curve_publickey" type = "key" mode = "rw" />
|
||||
<option name = "curve_secretkey" type = "key" mode = "rw" />
|
||||
<option name = "curve_serverkey" type = "key" mode = "rw" />
|
||||
|
||||
<!-- We don't test these as libzmq doesn't always support GSSAPI security -->
|
||||
<option name = "gssapi_server" type = "int" mode = "rw" />
|
||||
<option name = "gssapi_plaintext" type = "int" mode = "rw" />
|
||||
<option name = "gssapi_principal" type = "string" mode = "rw" />
|
||||
<option name = "gssapi_service_principal"
|
||||
type = "string" mode = "rw" />
|
||||
|
||||
<!-- New names for deprecated 3.x options -->
|
||||
<option name = "ipv6" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "immediate" type = "int" mode = "rw" test = "Dealer" />
|
||||
|
||||
<!-- Deprecated 3.x options -->
|
||||
<option name = "router_raw" type = "int" mode = "w" test = "Router">
|
||||
<restrict type = "Router" />
|
||||
</option>
|
||||
<option name = "ipv4only" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "delay_attach_on_connect"
|
||||
type = "int" mode = "w" test = "Pub" />
|
||||
|
||||
<!-- Options that are the same in 3.x -->
|
||||
<include name = "3-x options" />
|
||||
</version>
|
||||
|
||||
<version major = "3" style = "macro">
|
||||
<!-- Options that are carried forward to 4.0 -->
|
||||
<include name = "3-x options" />
|
||||
|
||||
<!-- Options that are deprecated in 4.0 -->
|
||||
<option name = "router_raw" type = "int" mode = "w" test = "Router">
|
||||
<restrict type = "Router" />
|
||||
</option>
|
||||
<option name = "ipv4only" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "delay_attach_on_connect"
|
||||
type = "int" mode = "w" test = "Pub" />
|
||||
</version>
|
||||
|
||||
<!-- Legacy version 2 -->
|
||||
<version major = "2" style = "macro">
|
||||
<option name = "hwm" type = "uint64" mode = "rw" test = "Sub" />
|
||||
<option name = "swap" type = "int64" mode = "rw" test = "Sub" />
|
||||
<option name = "affinity" type = "uint64" mode = "rw" test = "Sub" />
|
||||
<option name = "identity" type = "string" mode = "rw" test = "Sub" />
|
||||
<option name = "rate" type = "int64" mode = "rw" test = "Sub" />
|
||||
<option name = "recovery_ivl" type = "int64" mode = "rw" test = "Sub" />
|
||||
<option name = "recovery_ivl_msec" type = "int64" mode = "rw" test = "Sub" />
|
||||
<option name = "mcast_loop" type = "int64" mode = "rw" test = "Sub" />
|
||||
<option name = "rcvtimeo" type = "int" mode = "rw" test = "Sub" minor = "2" />
|
||||
<option name = "sndtimeo" type = "int" mode = "rw" test = "Sub" minor = "2" />
|
||||
<option name = "sndbuf" type = "uint64" mode = "rw" test = "Sub" />
|
||||
<option name = "rcvbuf" type = "uint64" mode = "rw" test = "Sub" />
|
||||
<option name = "linger" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "reconnect_ivl" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "reconnect_ivl_max" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "backlog" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "subscribe" type = "string" mode = "w" test = "Sub">
|
||||
<restrict type = "Sub" />
|
||||
</option>
|
||||
<option name = "unsubscribe" type = "string" mode = "w" test = "Sub">
|
||||
<restrict type = "Sub" />
|
||||
</option>
|
||||
<option name = "type" type = "int" mode = "r" test = "Sub" />
|
||||
<option name = "rcvmore" type = "int64" mode = "r" test = "Sub" />
|
||||
<option name = "fd" type = "int" mode = "r" test = "Sub" />
|
||||
<option name = "events" type = "uint32" mode = "r" test = "Sub" />
|
||||
</version>
|
||||
|
||||
<macro name = "3-x options">
|
||||
<option name = "type" type = "int" mode = "r" test = "Sub" />
|
||||
<option name = "sndhwm" type = "int" mode = "rw" test = "Pub" />
|
||||
<option name = "rcvhwm" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "affinity" type = "uint64" mode = "rw" test = "Sub" />
|
||||
<option name = "subscribe" type = "string" mode = "w" test = "Sub">
|
||||
<restrict type = "Sub" />
|
||||
</option>
|
||||
<option name = "unsubscribe" type = "string" mode = "w" test = "Sub">
|
||||
<restrict type = "Sub" />
|
||||
</option>
|
||||
<option name = "identity" type = "string" mode = "rw" test = "Dealer">
|
||||
<restrict type = "Req" />
|
||||
<restrict type = "Rep" />
|
||||
<restrict type = "Dealer" />
|
||||
<restrict type = "Router" />
|
||||
</option>
|
||||
<option name = "rate" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "recovery_ivl" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "sndbuf" type = "int" mode = "rw" test = "Pub" />
|
||||
<option name = "rcvbuf" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "linger" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "reconnect_ivl" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "reconnect_ivl_max" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "backlog" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "maxmsgsize" type = "int64" mode = "rw" test = "Sub" />
|
||||
<option name = "multicast_hops" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "rcvtimeo" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "sndtimeo" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "xpub_verbose" type = "int" mode = "w" test = "XPub">
|
||||
<restrict type = "XPub" />
|
||||
</option>
|
||||
<option name = "tcp_keepalive" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "tcp_keepalive_idle"
|
||||
type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "tcp_keepalive_cnt" type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "tcp_keepalive_intvl"
|
||||
type = "int" mode = "rw" test = "Sub" />
|
||||
<option name = "tcp_accept_filter" type = "string" mode = "rw" test = "Sub"
|
||||
test_value = "127.0.0.1" />
|
||||
<option name = "rcvmore" type = "int" mode = "r" test = "Sub" />
|
||||
<option name = "fd" type = "int" mode = "r" test = "Sub" />
|
||||
<option name = "events" type = "int" mode = "r" test = "Sub" />
|
||||
<option name = "last_endpoint" type = "string" mode = "r" test = "Sub" />
|
||||
</macro>
|
||||
</options>
|
|
@ -0,0 +1,10 @@
|
|||
# golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||
## explicit
|
||||
golang.org/x/net/internal/socks
|
||||
golang.org/x/net/proxy
|
||||
# gopkg.in/irc.v3 v3.1.4
|
||||
## explicit
|
||||
gopkg.in/irc.v3
|
||||
# gopkg.in/zeromq/goczmq.v4 v4.1.0
|
||||
## explicit
|
||||
gopkg.in/zeromq/goczmq.v4
|
Loading…
Reference in New Issue