sparoid 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e31fd430f74d9eabc1cae19dfe70b1ad40511850ecf77d1e5b436e5dfc206836
4
- data.tar.gz: 6fc3c4fa8f4a9e0cc2d63e26138e89b03c689481c1a3b42be46493aad653d2ae
3
+ metadata.gz: d077c30ec18d566719f9d594991798d8ecd94ac74938d5f5b21fb37a677066ca
4
+ data.tar.gz: b5f44ce465c4705dfdf5a0c705ad29603744be2145a831cd9c000be0ebb03ea0
5
5
  SHA512:
6
- metadata.gz: c445dc3e34df5a0a51c654d0e8447a7d96cb8ba675d105680bfa74a0b0fdedc98364123a375ac3fe7213e1c17200b1ce1181a19f944d22f4998d2f730239bfdd
7
- data.tar.gz: 3a91e79c4030419da559578051f4abef33aceaa8e20a3fd1a3202959342030941f5d6bb7c33e31fe1dfa2107dea2b63bcda1719eb2b50cd0551a23630b5f1c78
6
+ metadata.gz: e937c061cacbadf3206cffc1d96eb4f99dd667ce8daf1becb91db2eba8aa3893384dd31a59924acc255c11e39bd0aad3ebed7b5e29430c92356e3c54162d2d1b
7
+ data.tar.gz: 5ee4e40eef7d12ee5e052ca92452bdae45814f81260d10e35339710255eec8b8cd207fcba2cd31af73a35b50344a2d224cb5f237ce8c6a498b94b7c61249b61e
@@ -14,9 +14,9 @@ jobs:
14
14
  ruby: [ 3.2, 3.3, 3.4, 4.0, ruby-head ]
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
- - uses: actions/checkout@v4
18
- - uses: ruby/setup-ruby@v1
19
- with:
20
- ruby-version: ${{ matrix.ruby }}
21
- bundler-cache: true
22
- - run: bundle exec rake
17
+ - uses: actions/checkout@v6
18
+ - uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby }}
21
+ bundler-cache: true
22
+ - run: bundle exec rake
@@ -0,0 +1,20 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v6
16
+ - uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: '3.4'
19
+ bundler-cache: true
20
+ - uses: rubygems/release-gem@v1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [2.2.0] - 2026-05-06
2
+
3
+ - Raise when sendmsg fails for every resolved address (#23)
4
+
5
+ ## [2.1.2] - 2026-04-30
6
+
7
+ - Skip addresses that fail synchronous connect in fdpass (#20)
8
+
9
+ ## [2.1.1] - 2026-04-13
10
+
11
+ - Remove the usage of Timeout (#19)
12
+
1
13
  ## [2.1.0] - 2026-03-10
2
14
 
3
15
  - Aligning CLI with Crystal client interface (`send`/`connect` subcommands with flags)
data/README.md CHANGED
@@ -34,7 +34,15 @@ Host *.example.com
34
34
 
35
35
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
36
36
 
37
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
37
+ To install this gem onto your local machine, run `bundle exec rake install`.
38
+
39
+ To release a new version:
40
+
41
+ 1. Bump `Sparoid::VERSION` in `lib/sparoid/version.rb` and add an entry to `CHANGELOG.md`.
42
+ 2. Open a PR with the bump, get it merged into `main`.
43
+ 3. Push a tag matching the new version, e.g. `git tag v2.1.2 && git push origin v2.1.2`.
44
+
45
+ The `Release` GitHub Actions workflow then builds the gem and publishes it to [rubygems.org](https://rubygems.org) via OIDC trusted publishing — no API key required.
38
46
 
39
47
  ## Contributing
40
48
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sparoid
4
- VERSION = "2.1.1"
4
+ VERSION = "2.2.0"
5
5
  end
data/lib/sparoid.rb CHANGED
@@ -22,20 +22,27 @@ module Sparoid # rubocop:disable Metrics/ModuleLength
22
22
  # Send an authorization packet
23
23
  def auth(key, hmac_key, host, port, open_for_ip: nil)
24
24
  addrs = resolve_ip_addresses(host, port)
25
- addrs.each do |addr|
25
+ errors = []
26
+ successful_addrs = addrs.filter_map do |addr|
26
27
  messages = generate_messages(open_for_ip)
27
28
  data = messages.map do |message|
28
29
  prefix_hmac(hmac_key, encrypt(key, message))
29
30
  end
30
31
  sendmsg(addr, data)
32
+ addr
33
+ rescue SystemCallError => e
34
+ errors << "#{addr.ip_address}: #{e.message}"
35
+ nil
31
36
  end
32
37
 
38
+ raise Error, "Sparoid failed to send to any address for #{host}: #{errors.join("; ")}" if successful_addrs.empty?
39
+
33
40
  # wait some time for the server to actually open the port
34
41
  # if we don't wait the next SYN package will be dropped
35
42
  # and it have to be redelivered, adding 1 second delay
36
43
  sleep 0.02
37
44
 
38
- addrs
45
+ successful_addrs
39
46
  end
40
47
 
41
48
  # Generate new aes and hmac keys, print to stdout
@@ -48,12 +55,18 @@ module Sparoid # rubocop:disable Metrics/ModuleLength
48
55
  end
49
56
 
50
57
  # Connect to a TCP server and pass the FD to the parent
51
- def fdpass(addrs, port, connect_timeout: 10) # rubocop:disable Metrics/AbcSize
52
- # try connect to all IPs
53
- sockets = addrs.map do |addr|
54
- Socket.new(addr.afamily, Socket::SOCK_STREAM).tap do |s|
55
- s.connect_nonblock(Socket.sockaddr_in(port, addr.ip_address), exception: false)
56
- end
58
+ def fdpass(addrs, port, connect_timeout: 10) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
59
+ # try connect to all IPs; skip addrs that fail synchronously (e.g. no route)
60
+ viable_addrs = []
61
+ sockets = addrs.filter_map do |addr|
62
+ s = Socket.new(addr.afamily, Socket::SOCK_STREAM)
63
+ s.connect_nonblock(Socket.sockaddr_in(port, addr.ip_address), exception: false)
64
+ viable_addrs << addr
65
+ s
66
+ rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ECONNREFUSED => e
67
+ warn "Sparoid: skip #{addr.ip_address}: #{e.message}"
68
+ s&.close
69
+ nil
57
70
  end
58
71
  # wait for any socket to be connected
59
72
  until sockets.empty?
@@ -62,7 +75,7 @@ module Sparoid # rubocop:disable Metrics/ModuleLength
62
75
  writeable.each do |s|
63
76
  idx = sockets.index(s)
64
77
  sockets.delete_at(idx) # don't retry this socket again
65
- addr = addrs.delete_at(idx) # find the IP for the socket
78
+ addr = viable_addrs.delete_at(idx) # find the IP for the socket
66
79
  begin
67
80
  s.connect_nonblock(Socket.sockaddr_in(port, addr.ip_address)) # check for errors
68
81
  rescue Errno::EISCONN
@@ -99,11 +112,9 @@ module Sparoid # rubocop:disable Metrics/ModuleLength
99
112
  socket.nonblock = false
100
113
  data.each do |packet|
101
114
  socket.sendmsg packet, 0, addr
102
- rescue StandardError => e
103
- warn "Sparoid error: #{e.message}"
104
115
  end
105
116
  ensure
106
- socket.close
117
+ socket&.close
107
118
  end
108
119
 
109
120
  def encrypt(key, data)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sparoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carl Hörberg
@@ -17,6 +17,7 @@ extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
19
  - ".github/workflows/main.yml"
20
+ - ".github/workflows/release.yml"
20
21
  - ".gitignore"
21
22
  - ".rubocop.yml"
22
23
  - CHANGELOG.md