-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
this is required by bindings to languages whose networking libraries main abstraction are berkley sockets, not connections as in Go
- Loading branch information
1 parent
3afc9a9
commit 2b4324c
Showing
8 changed files
with
590 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Launch Package", | ||
"type": "go", | ||
"request": "launch", | ||
"mode": "auto", | ||
"program": "${workspaceFolder}/_examples/helloworld_socket/helloworld_socket.go", | ||
"args": ["-remote" ,"19-ffaa:1:1067,127.0.0.1:2222"] | ||
|
||
} | ||
] | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// Copyright 2018 ETH Zurich | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package main | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"flag" | ||
"fmt" | ||
"net/netip" | ||
"os" | ||
"time" | ||
|
||
"github.com/netsec-ethz/scion-apps/pkg/pan" | ||
) | ||
|
||
func main() { | ||
var err error | ||
// get local and remote addresses from program arguments: | ||
var listen pan.IPPortValue | ||
flag.Var(&listen, "listen", "[Server] local IP:port to listen on") | ||
remoteAddr := flag.String("remote", "", "[Client] Remote (i.e. the server's) SCION Address (e.g. 17-ffaa:1:1,[127.0.0.1]:12345)") | ||
count := flag.Uint("count", 1, "[Client] Number of messages to send") | ||
flag.Parse() | ||
|
||
if (listen.Get().Port() > 0) == (len(*remoteAddr) > 0) { | ||
check(fmt.Errorf("either specify -listen for server or -remote for client")) | ||
} | ||
|
||
if listen.Get().Port() > 0 { | ||
err = runServer(listen.Get()) | ||
check(err) | ||
} else { | ||
err = runClient(*remoteAddr, int(*count)) | ||
check(err) | ||
} | ||
} | ||
|
||
func runServer(listen netip.AddrPort) error { | ||
sock, err := pan.NewScionSocket(context.Background(), listen) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
defer sock.Close() | ||
fmt.Println(sock.LocalAddr()) | ||
|
||
buffer := make([]byte, 16*1024) | ||
for { | ||
n, from, err := sock.ReadFrom(buffer) | ||
if err != nil { | ||
return err | ||
} | ||
data := buffer[:n] | ||
fmt.Printf("Received %s: %s\n", from, data) | ||
msg := fmt.Sprintf("take it back! %s", time.Now().Format("15:04:05.0")) | ||
n, err = sock.WriteTo([]byte(msg), from) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Printf("Wrote %d bytes.\n", n) | ||
} | ||
} | ||
|
||
func runClient(address string, count int) error { | ||
addr, err := pan.ResolveUDPAddr(context.TODO(), address) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
sock, err := pan.NewScionSocket(context.Background(), netip.AddrPort{}) | ||
if err != nil { | ||
return err | ||
} | ||
defer sock.Close() | ||
|
||
for i := 0; i < count; i++ { | ||
nBytes, err := sock.WriteTo([]byte(fmt.Sprintf("hello world %s", time.Now().Format("15:04:05.0"))), addr) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Printf("Wrote %d bytes.\n", nBytes) | ||
|
||
buffer := make([]byte, 16*1024) | ||
/* if err = conn.SetReadDeadline(time.Now().Add(1 * time.Second)); err != nil { | ||
return err | ||
} */ | ||
n, _, err := sock.ReadFrom(buffer) | ||
if errors.Is(err, os.ErrDeadlineExceeded) { | ||
continue | ||
} else if err != nil { | ||
return err | ||
} | ||
data := buffer[:n] | ||
fmt.Printf("Received reply: %s\n", data) | ||
} | ||
return nil | ||
} | ||
|
||
// Check just ensures the error is nil, or complains and quits | ||
func check(e error) { | ||
if e != nil { | ||
fmt.Fprintln(os.Stderr, "Fatal error:", e) | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package pan | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
) | ||
|
||
// socket roles (selector internals) | ||
const dialer = 0 | ||
const listener = 1 | ||
|
||
/* | ||
! | ||
\brief this class provides the service of path selection for a remote address to scion-sockets | ||
Every scion-socket has one. | ||
It should not be cluttered with Refresh()/Update()/PathDown or any other technical | ||
methods that are required by the pathStatedDB or pathPool to update the selectors state. | ||
*/ | ||
type CombiSelector interface { | ||
Close() error | ||
// PathDown(PathFingerprint, PathInterface) // pathDownNotifyee | ||
|
||
// called when the scion-socket is bound to an address | ||
LocalAddrChanged(newlocal UDPAddr) | ||
Path(remote UDPAddr) (*Path, error) | ||
//Refresh([]*Path) Selector | ||
// Refresh(paths []*Path, remote UDPAddr) | ||
Record(remote UDPAddr, path *Path) | ||
//Update( )# | ||
// setter the respective defaults | ||
SetReplySelector(ReplySelector) | ||
SetSelector(sel func() Selector) | ||
SetPolicy(pol func() Policy) | ||
|
||
// setter for AS specific path policies | ||
// SetSelectorFor(remote IA, sel Selector) | ||
// SetPolicyFor(remote IA, pol Policy) | ||
// SetPolicedSelectorFor(remote IA, sel Selector, pol Policy) | ||
|
||
// initialize(local UDPAddr, remote UDPAddr, paths []*Path) | ||
// maybe make this pubilc and let the ScionCocketCall it, | ||
// in its ctor (or once the local addr is known i.e after Bind() was called ) | ||
} | ||
|
||
type DefaultCombiSelector struct { | ||
local UDPAddr | ||
roles map[UDPAddr]int // is this host the dialer or listener for the connection to this remote host | ||
// decided from which method is called first for a remote address X | ||
// Record(X)->listener or Path(X)->dialer | ||
|
||
// maybe make map of pair (, ,) ?! | ||
// policies map[UDPAddr]Policy | ||
policy_factory func() Policy | ||
selector_factory func() Selector | ||
|
||
// TODO: this state should be confined in size somehow | ||
// i.e. drop selectors with LRU scheme | ||
// Note that this is not an attack vector, as this state can only be increased | ||
// by deliberate decisions of this host to dial a remote for which it does not yet has a selector | ||
selectors map[IA]Selector | ||
subscribers map[IA]*pathRefreshSubscriber | ||
replyselector ReplySelector | ||
} | ||
|
||
func (s *DefaultCombiSelector) needPathTo(remote UDPAddr) bool { | ||
return s.local.IA != remote.IA | ||
} | ||
|
||
func (s *DefaultCombiSelector) Close() error { | ||
|
||
for _, v := range s.subscribers { | ||
if e := v.Close(); e != nil { | ||
return e | ||
} | ||
} | ||
|
||
for _, v := range s.selectors { | ||
if e := v.Close(); e != nil { | ||
return e | ||
} | ||
} | ||
if e := s.replyselector.Close(); e != nil { | ||
return e | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func NewDefaultCombiSelector(local UDPAddr) (CombiSelector, error) { | ||
selector := &DefaultCombiSelector{ | ||
local: local, | ||
roles: make(map[UDPAddr]int), | ||
// policies: make(map[UDPAddr]Policy), | ||
policy_factory: func() Policy { | ||
var s Policy = nil | ||
return s | ||
}, | ||
selector_factory: func() Selector { return NewDefaultSelector() }, | ||
selectors: make(map[IA]Selector), | ||
subscribers: make(map[IA]*pathRefreshSubscriber), | ||
replyselector: NewDefaultReplySelector(), | ||
} | ||
|
||
selector.replyselector.Initialize(local) | ||
|
||
return selector, nil | ||
} | ||
|
||
func (s *DefaultCombiSelector) SetReplySelector(rep ReplySelector) { | ||
s.replyselector = rep | ||
} | ||
|
||
func (s *DefaultCombiSelector) LocalAddrChanged(newlocal UDPAddr) { | ||
s.local = newlocal | ||
} | ||
|
||
func (s *DefaultCombiSelector) Path(remote UDPAddr) (*Path, error) { | ||
if r, ok := s.roles[remote]; ok { | ||
// the role is already decided | ||
if r == dialer { | ||
if s.needPathTo(remote) { | ||
sel := s.selectors[remote.IA] | ||
sel.NewRemote(remote) | ||
return sel.Path(remote) | ||
} else { | ||
return nil, errors.New("if src and dst are in same AS and no scion path is required, the connection shouldnt request one") | ||
} | ||
} else { | ||
return s.replyselector.Path(remote) | ||
} | ||
} else { | ||
// no role yet -> no path to remote has been requested yet Path() | ||
// so we are acting as a server | ||
s.roles[remote] = dialer | ||
|
||
// set up a refresherSubscriber etc .. | ||
if s.needPathTo(remote) { | ||
var selector Selector | ||
var policy Policy | ||
|
||
if s.policy_factory != nil { | ||
policy = s.policy_factory() | ||
} | ||
if s.selector_factory != nil { | ||
selector = s.selector_factory() | ||
} else { | ||
selector = NewDefaultSelector() | ||
} | ||
var ctx context.Context = context.Background() | ||
// Todo: set timeout for path request | ||
subscriber, err := openPathRefreshSubscriber(ctx, s.local, remote, policy, selector) | ||
if err != nil { | ||
|
||
return nil, err | ||
} | ||
s.selectors[remote.IA] = selector | ||
s.subscribers[remote.IA] = subscriber | ||
|
||
return selector.Path(remote) | ||
} else { | ||
return nil, errors.New("if src and dst are in same AS and no scion path is required, the connection shouldnt request one") | ||
} | ||
} | ||
} | ||
|
||
func (s *DefaultCombiSelector) SetPolicy(pol func() Policy) { | ||
s.policy_factory = pol | ||
} | ||
|
||
func (s *DefaultCombiSelector) SetSelector(sel func() Selector) { | ||
s.selector_factory = sel | ||
} | ||
|
||
func (s *DefaultCombiSelector) Record(remote UDPAddr, path *Path) { | ||
|
||
if r, ok := s.roles[remote]; ok { | ||
// the role is already decided | ||
if r == listener { | ||
s.replyselector.Record(remote, path) | ||
} | ||
} else { | ||
// no role yet -> no path to remote has been requested yet Path() | ||
// so we are acting as a server | ||
s.roles[remote] = listener | ||
} | ||
|
||
} |
Oops, something went wrong.