Could not resolve host: rest.nexmo.com
sts-ryan-holton opened this issue · 18 comments
Hi, just checking here as I'd opened an issue on the Laravel Vonage notification channel here suggesting that I open it here. Basically, I'm running the latest version of the vonage notification channel and I'm seeing failed jobs relating to:
Could not resolve host: rest.nexmo.com
I wonder if there's references to this in your code somewhere?
Here's my error:
GuzzleHttp\Exception\ConnectException: cURL error 6: Could not resolve host: rest.nexmo.com (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://rest.nexmo.com/sms/json in /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php:210
Stack trace:
#0 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php(158): GuzzleHttp\Handler\CurlFactory::createRejection()
#1 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php(110): GuzzleHttp\Handler\CurlFactory::finishError()
#2 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php(47): GuzzleHttp\Handler\CurlFactory::finish()
#3 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php(28): GuzzleHttp\Handler\CurlHandler->__invoke()
#4 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php(48): GuzzleHttp\Handler\Proxy::GuzzleHttp\Handler\{closure}()
#5 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php(64): GuzzleHttp\Handler\Proxy::GuzzleHttp\Handler\{closure}()
#6 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Middleware.php(31): GuzzleHttp\PrepareBodyMiddleware->__invoke()
#7 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php(55): GuzzleHttp\Middleware::GuzzleHttp\{closure}()
#8 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Middleware.php(63): GuzzleHttp\RedirectMiddleware->__invoke()
#9 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/HandlerStack.php(75): GuzzleHttp\Middleware::GuzzleHttp\{closure}()
#10 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Client.php(333): GuzzleHttp\HandlerStack->__invoke()
#11 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Client.php(107): GuzzleHttp\Client->transfer()
#12 /var/www/snapisms-api/vendor/guzzlehttp/guzzle/src/Client.php(138): GuzzleHttp\Client->sendAsync()
#13 /var/www/snapisms-api/vendor/vonage/client-core/src/Client.php(475): GuzzleHttp\Client->sendRequest()
#14 /var/www/snapisms-api/vendor/vonage/client-core/src/Client/APIResource.php(123): Vonage\Client->send()
#15 /var/www/snapisms-api/vendor/vonage/client-core/src/SMS/Client.php(49): Vonage\Client\APIResource->create()
#16 /var/www/snapisms-api/vendor/laravel/vonage-notification-channel/src/Channels/VonageSmsChannel.php(71): Vonage\SMS\Client->send()
@SecondeJK Just bumping due to urgency here
Hi,
rest.nexmo.com is definitely not down, and Guzzle is complaining that it cannot complete the request - the code is APIResource hasn't changed (and I can make requests). This points to a problem with your server configuration: I think it's likely to be a firewall rule. Are you able to access/SSH the server and make a cURL request from the command line?
If I do:
curl https://rest.nexmo.com/sms/json
I get:
{"message-count":"1","messages":[{"status":"2","error-text":"Missing api_key"}]}
Is this correct? I can try directly within my server if correcT
Curl seem to be working from within my SSH server with the above
OK, it's good news that curl is working.
This therefore points to a misconfiguration of Guzzle, or more likely a problem with your hosts file / DNS.
See this thread here within the guzzle project and see if there are some solutions in there, such as adding it to your /etc/hosts file if it's a Unix-like server:
guzzle/guzzle#1996
Interestingly, I just tried it without doing anything in my code and it's working - I think there's an intermittent problem here, but I don't know what. Not experiencing the problem on other Laravel projects using the same Vonage notification package version. I'm using CentOS
This issue has also now been reported on the laravel service provider wrapper library. I'll start doing some digging.
The vonage base client allows you to pass in any PSR-18 client. If you don't pass one in, it configures Guzzle for you and injects it. Can I check if you're passing in an HTTP client or not? Thanks!
Can I also ask what cloud provider you're using for hosting? It might be worth looking into if they've changed any firewall or DNS rules.
I'm using the newest version of vonage-notification-channel, I'm using CentOS Stream version 9 on Digital Ocean with firewalld installed, but I have two separate projects, both use the vonage channel, I don't seem to get the error on every request, but getting quite a few. My website goes through Cloudflare, here's my Laravel notification file called SendMessageViaVonageEngine
if it's of use:
<?php
namespace App\Notifications\Messages;
use App\Models\CreditTransaction;
use App\Models\Message;
use App\Models\Sanctum\PersonalAccessToken;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\VonageMessage;
use Illuminate\Notifications\Notification;
class SendMessageViaVonageEngine extends Notification implements ShouldQueue
{
use Queueable;
/**
* The message instance
*/
protected $message;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct($message)
{
$this->message = $message;
}
/**
* Determine which queues should be used for each notification channel.
*/
public function viaQueues(): array
{
return [
'vonage' => 'messages-send',
];
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
*/
public function via($notifiable): array
{
return ['vonage'];
}
/**
* Get the Vonage / SMS representation of the notification.
*
* @param mixed $notifiable
*/
public function toVonage($notifiable): VonageMessage
{
$message = $this->getMessage();
if (! $message) {
exit;
return [];
}
if (! $this->hasSufficientBalance()) {
$this->updateMessageStatus($message, 'failed', 'Insufficient credits');
exit;
return [];
}
$this->updateMessageStatus($message, 'sent', 'Message sent');
try {
$amount = $this->getNumberOfDeductableCredits($this->message->data['body']);
$this->createCreditTransaction($message, $amount);
return (new VonageMessage)
->clientReference($this->message->id)
->content($this->message->data['body']);
} catch (\Exception $e) {
$this->updateMessageStatus($message, 'failed', 'Cannot update balance');
exit;
return [];
}
}
/**
* Get number of credits to deduct
*/
public function getNumberOfDeductableCredits($body)
{
try {
// calculate the number of parts
$smsCalculator = new \App\Libraries\Messente\SmsLengthCalculator();
$smsParts = $smsCalculator->getPartCount($body);
$amount = -intval($smsParts);
if ($amount >= 0) {
return -1;
}
return -intval($smsParts);
} catch (\Exception $e) {
// if there's an error, let's just deduct two credits to be
// completely safe here.
return -2;
}
}
/**
* Get the message
*/
public function getMessage()
{
return Message::where('id', $this->message->id)
->where('user_id', $this->message->user_id)
->first();
}
/**
* Do we have enough credit balance?
*/
public function hasSufficientBalance()
{
try {
$token = PersonalAccessToken::where('uuid', $this->message->api_key_uuid)
->where('tokenable_id', $this->message->user_id)
->withSum('creditTransactions AS credit_balance', 'delta')
->first();
if (! $token->credit_balance || $token->credit_balance <= 0) {
return false;
}
return true;
} catch (\Exception $e) {
}
return false;
}
/**
* Create a new credit transaction
*/
public function createCreditTransaction($message, $amount = -1)
{
$transaction = new CreditTransaction;
$transaction->user_id = $message->user_id;
$transaction->api_key_uuid = $message->api_key_uuid;
$transaction->delta = $amount;
$transaction->message_id = $message->id;
$transaction->processed_at = Carbon::now();
$transaction->save();
}
/**
* Update message status
*/
public function updateMessageStatus($message, $status = 'sent', $reason = null)
{
$message->status = $status;
$message->reason = $reason;
$message->sent_at = Carbon::now();
if ($status == 'failed' || $status == 'dead') {
$message->failed_at = Carbon::now();
}
$message->save();
}
}
And here's my project's dependencies:
{
"name": "laravel/laravel",
"type": "project",
"description": "The Laravel Framework.",
"keywords": [
"framework",
"laravel"
],
"license": "MIT",
"require": {
"php": "^8.1",
"consoletvs/profanity": "dev-master",
"dompdf/dompdf": "2.0.3",
"guzzlehttp/guzzle": "^7.5",
"laravel/cashier": "14.12.3",
"laravel/fortify": "1.17.4",
"laravel/framework": "10.14.1",
"laravel/sanctum": "3.2.5",
"laravel/telescope": "4.15.0",
"laravel/tinker": "^2.8",
"laravel/vonage-notification-channel": "3.2.1",
"mxl/laravel-queue-rate-limit": "dev-master",
"nesbot/carbon": "^2.64",
"propaganistas/laravel-disposable-email": "2.2.9",
"propaganistas/laravel-phone": "5.0.3",
"rinvex/countries": "9.0.1",
"sentry/sentry-laravel": "3.6.1",
"spatie/laravel-schedule-monitor": "3.3.0",
"symfony/http-client": "^6.2",
"symfony/mailgun-mailer": "^6.2"
},
"require-dev": {
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^7.1",
"phpunit/phpunit": "^10.0",
"fakerphp/faker": "^1.9.1",
"spatie/laravel-ignition": "^2.0"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
]
},
"extra": {
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
}
Ok, I think I've got to the root of the problem.
We made a CNAME change to rest.nexmo.com on 22nd June (which when this will have started)
For some reason, Digital Ocean users are specifically affected by this. If you can wipe your DNS cache or open a ticket with DO to reload your DNS records that will sort it
Does this mean you've made a change? Hopefully it'll auto update soon my end?
We have made a change, but a change that is a fairly routine infrastructure change that servers should be able to handle. At the moment we're seeing specifically problems with Digital Ocean droplets that have cached DNS records when they need to expire and reload. There's not really anything we can do our end here: you could open a ticket with DO but keep me posted
I'll open a support ticket in Digital Ocean and let you know my update
I've received back information from Digital Ocean. They've said:
Unfortunately, at DigitalOcean, we do not have direct access to your droplets to clear the DNS cache for you. However, this is something you can do yourself, by accessing your droplet via SSH and using the appropriate commands, typically systemd-resolve --flush-caches or service nscd restart.
So, I've gone ahead and installed nscd
on my droplet via SSH and have ran sudo yum install nscd
I've since given it 5 minutes and since attempted a new message send.
I'm still getting the same error
Just installing it won't actually do anything: you'll need to actually run the command on the server to flush the cache, and also restarting the service as per digital ocean's response
It's been a little while now since running the restart command that Digital Ocean suggested, I did run service nscd restart
on my last comment but forgot to mention it. I've since given it some time, some more messages have attempted to send today now with the same response:
GuzzleHttp\Exception\ConnectException: cURL error 6: Could not resolve host: rest.nexmo.com
I'll have to close this as this is a specific problem with Digital Ocean's servers that isn't related to the Vonage PHP SDK or the Vonage APIs. However, you might want to check out this thread: Vonage/vonage-laravel#20 which details the exact same issue.